Write a Node.js script that connects to a Notion database and writes its page and block content to a local JSON file.
This is part of a four-part series on showing the potential of Notion as a CMS for complex websites:
We're going to write a Node.js script that fetches pages from a Notion database and writes the result to a JSON file.
Because block information is not included in the page list response, we'll adjust the script to fetch blocks for each page and store them in a children
property on the page object, before writing the end result to a JSON file.
You'll need the following for this tutorial:
id
property of the database from Notion's API. This is not retrievable from the database's URL. This process is also covered in the previous post.Make sure you have a blank file to add your script. Then there are just a couple other quick setup tasks.
We're going to install the Notion JavaScript SDK so that we can easily interact with their API.
npm install @notionhq/client
Add the following environment variables, which should be accessible by the script:
NOTION_DATABASE_ID
: The id
property from the Notion database, retrieved via the API.NOTION_API_KEY
: Secret token from the Notion integration,First, let's write a script that simply retrieves the pages from the database and writes them to a notion-export.json
file in the same directory as the script.
const { Client } = require("@notionhq/client");
const fs = require("fs");
const path = require("path");
const notion = new Client({ auth: process.env.NOTION_API_KEY });
async function importPages() {
// Retrieve pages from the database.
let { results: pages } = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
});
// TODO -> Attach blocks to pages
// Write the result to file.
const outputFile = path.join(__dirname, "notion-export.json");
fs.writeFileSync(outputFile, JSON.stringify(pages, null, 2));
console.log(`Wrote ${pages.length} pages to ${outputFile}`);
}
importPages();
After running the script, you should see a new notion-export.json
file in the same directory as the script.
Open it up to inspect the results. Notice that it seems fairly sparse for how much information Notion likely has about a page. That will change when we add blocks to pages.
The script that retrieves the pages likely ran super fast. It only hit the database a single time. But, for blocks within a page, we have to call the API for every page, and again for every block with children.
I recommend trying this on a small scale first. Use a database with a small number of pages, and pages with a small number of blocks. Your approach and code will likely have to change as your databases scale.
Here's the full script:
const { Client } = require("@notionhq/client");
const fs = require("fs");
const path = require("path");
const notion = new Client({ auth: process.env.NOTION_API_KEY });
async function getBlocks(block_id) {
let { results: children } = await notion.blocks.children.list({ block_id });
for (const child of children) {
const grandchildren = await getBlocks(child.id);
child.children = grandchildren;
}
return children;
}
async function importPages() {
// Retrieve pages from the database.
let { results: pages } = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
});
// Attach blocks to pages
for (const page of pages) {
const blocks = await getBlocks(page.id);
page.children = blocks;
}
// Write the result to file.
const outputFile = path.join(__dirname, "notion-export.json");
fs.writeFileSync(outputFile, JSON.stringify(pages, null, 2));
console.log(`Wrote ${pages.length} pages to ${outputFile}`);
}
importPages();
Run it again and if your pages have blocks, you'll likely see a lot more content in the JSON file.
Here is an example project with this code.
If you're following the four-part series, Part 4 is the next and final step of the process, which takes this raw content and transforms it into a more usable object.