Created
February 10, 2026 22:03
-
-
Save ian-pvd/95e66193f8a3767251e7de2d0e43e76f to your computer and use it in GitHub Desktop.
A node script that leverages the Notion CLI to export WordPress markup from a Notion database.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Notion Export | |
| * | |
| * Export WP block markup from a Notion data source. | |
| * | |
| * Usage: | |
| * - Set environment variables: | |
| * NOTION_TOKEN=your_notion_integration_token | |
| * DATA_SOURCE_ID=your_notion_data_source_id | |
| * | |
| * Run with: | |
| * node notion-export.js | |
| * | |
| * @requires @notionhq/client | |
| * | |
| * @see https://developers.notion.com/reference/query-a-data-source | |
| */ | |
| const fs = require("fs"); | |
| const { Client } = require("@notionhq/client"); | |
| // Initialize Notion client with auth token. | |
| const notion = new Client({ | |
| auth: process.env.NOTION_TOKEN | |
| }); | |
| /** | |
| * Get text from Notion property types. | |
| * | |
| * @param {object} prop - Notion property object | |
| * @returns {string} Extracted text value | |
| * | |
| * @see https://developers.notion.com/reference/property-object | |
| */ | |
| function getText(prop) { | |
| // Early return for empty properties. | |
| if (!prop) return ""; | |
| // Handle rich text | |
| if (prop.rich_text) return prop.rich_text.map(t => t.plain_text).join(""); | |
| // Handle URL | |
| if (prop.url) { | |
| return prop.url; | |
| } | |
| // Handle rollup | |
| if (prop.rollup) { | |
| // Handle rollup arrays | |
| if (prop.rollup.array?.length) { | |
| return prop.rollup.array | |
| .map(item => item.title?.[0]?.plain_text || item.rich_text?.[0]?.plain_text || "") | |
| .join(", "); | |
| } | |
| // Handle text rollup | |
| if (prop.rollup.type === "text" && prop.rollup.text) { | |
| return prop.rollup.text; | |
| } | |
| } | |
| // Handle select and multi-select | |
| if (prop.select?.name) return prop.select.name; | |
| if (prop.multi_select?.length) | |
| return prop.multi_select.map(s => s.name).join(", "); | |
| return ""; | |
| } | |
| (async () => { | |
| // Validate the request. | |
| // @see https://nodejs.org/api/process.html#processenv | |
| if (!process.env.DATA_SOURCE_ID) { | |
| console.error("Error: DATA_SOURCE_ID environment variable is not set."); | |
| process.exit(1); | |
| } | |
| // Request data from a Notion date source. | |
| const dataSourceId = process.env.DATA_SOURCE_ID; | |
| const response = await notion.dataSources.query({ | |
| data_source_id: dataSourceId, | |
| filter: { | |
| property: "To Export", | |
| checkbox: { equals: true } | |
| }, | |
| page_size: 20 | |
| }); | |
| console.log(`# Results: ${response.results.length}`); | |
| // Initialize HTML output. | |
| let html = ""; | |
| // Prepend markup before the notion export html. | |
| // For example, into text or custom styles. | |
| html += `<!-- wp:html --> | |
| <style type="text/css"> | |
| .wp-block-group h3 { | |
| color: #333; | |
| margin-bottom: 0.5em; | |
| } | |
| </style> | |
| <!-- /wp:html --> | |
| `; | |
| // Create an item count for logging. | |
| let itemCount = 1; | |
| // For each result, get properties and append HTML. | |
| for (const page of response.results) { | |
| const properties = page.properties; | |
| const title = getText(properties.Title); | |
| const creator = getText(properties.Creator); | |
| const sourceUrl = getText(properties.SourceURL); | |
| const embedRef = getText(properties.EmbedReference); | |
| console.log(`${itemCount++} Item: ${title}`); | |
| // Append the WordPress block markup to the HTML export. | |
| html += ` | |
| <!-- wp:group {"layout":{"type":"constrained"}} --> | |
| <div class="wp-block-group"> | |
| <!-- wp:heading {"level":3} --> | |
| <h3 class="wp-block-heading">${title}<br>by ${creator}</h3> | |
| <!-- /wp:heading --> | |
| <!-- wp:html --> | |
| <iframe style="border: 0; width: 100%; height: 120px;" src="${embedRef}" seamless><a href="${sourceUrl}">${title}</a></iframe> | |
| <!-- /wp:html --> | |
| <!-- wp:buttons --> | |
| <div class="wp-block-buttons"> | |
| <!-- wp:button --> | |
| <div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="${sourceUrl}" target="_blank" rel="noreferrer noopener">View Source</a></div> | |
| <!-- /wp:button --> | |
| </div> | |
| <!-- /wp:buttons --> | |
| </div> | |
| <!-- /wp:group --> | |
| `; | |
| } | |
| // Generate a unique, sortable timestamp suffix for the file name. | |
| const timestamp = new Date().toISOString().replace(/[:.]/g, "-"); | |
| fs.writeFileSync(`notion-export-${timestamp}.html`, html, "utf8"); | |
| // console.log(html); | |
| // console.log(response); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment