How to Create, Update, and Delete Data in a Notion Database using Node JS

August 24, 2022
Written by
Reviewed by

Header image for notion database tutorial post

Notion is a popular productivity tool used widely in both professional and personal settings. As of May 2021, Notion released their very own public beta JavaScript SDK to use with their API. At the time of writing, it is installable via npm and makes the process of working with Notion much more straightforward than it was before.

This tutorial explores the basics of manipulating data in a Notion database using their API and SDK with NodeJS. This tutorial aims to be beginner-friendly and will have explanations of every step. So without further ado, let’s get started!

Prerequisites

Before beginning, ensure you have the following:

  • The latest version of NodeJS installed on your computer
  • A Notion account – this tutorial assumes a free personal account
  • An IDE – I will be using VSCode

Initial setup

There are two things that need to be set up in order to follow along with this tutorial. The first thing is to create a Notion database using Notions UI, and the second part will deal with setting up the local environment and project files.

This guide is written with a free Notion personal account in mind. If any steps seem to show settings or features that don’t appear on your end, try them with a free account instead of a company or workspace account to see if things change.

Create the Notion database

For the purposes of this tutorial, the database will be set up to have four columns to represent a few different types of data for each entry. If this is a brand new account, there will be a number of pages available out of the box, as is shown in the screenshot below. Feel free to delete them or keep them.

Where to find add a page when first accessing a new account

Once logged into Notion, create a new page by clicking the gray + Add a page button at the bottom of the left sidebar.

If your Notion account email is associated with a company, ensure that your new page is in the PRIVATE section of the sidebar and not WORKSPACE, otherwise other people in your company will be able to view your database. If your account is not associated with a company, there will only be a default, unlabelled section for all your pages.

Once the new page is created, a new blank page will open up in the main editor area. Replace “Untitled” title with “Notion Database” and click Table under the DATABASE option.

After selecting Table, a new view will appear showing that there is no data source for the database. On this page, go into the right sidebar, and at the very bottom select New Database.

Converting the new default page into a notion database

This will create a default database with some columns already filled in. First, delete all the rows by selecting on the six dots on the left side of each row and click Delete.

How to delete a row in a notion database

Next, the columns will need to be edited. Change the first column name to ID; the type cannot be changed from text. This column will hold a unique, username-like value that will be used to query the database later on. Change the second column's name to Name and set its type to text as well. Add a third column called Status and set the type to checkbox. Finally, add the fourth and final column called Date with the type date.

Now, the content of the database should just be the labeled columns with the table content being text that says “Empty table.”, as in the screenshot below.

The empty Notion database

Retrieve the Notion API key

To find your Notion API key, navigate to the Notion API page and in the top right corner, click on the button labeled View my integrations. Once there, create a new integration by clicking the dedicated tile labeled Create new integration or the button in the left sidebar labeled + New integration.

A picture of where to find new integrations buttons

Creating a new integration opens a setup page where it can be named and given certain permissions. In my case:

  • I named my integration "Demo".
  • My Associated workspace defaulted to my personal account. If you are using a specific workspace be sure to change it to the one that contains your database.
  • I checked the three checkboxes under Content Capabilities.
  • I left the two checkboxes under Comment Capabilities unchecked.
  • Finally, I set User Capabilities to the last option that allows our integration to read user information including email addresses.

Integration settings screenshot

Once the settings for the integration have been set, click the button at the bottom labeled Submit.

After submitting, you will be redirected to a new page that contains your Notion API key, also called the Internal Integration Token. Clicking the blue Show button on the right hand side of the API key will expose it so that you can use it later in the tutorial. Copy it and keep it handy.

Connect your integration and retrieve the database ID

Navigate back to your Notion database page. In the top right corner of the page you will see a button made of three black dots. Click on it and it will open a dropdown menu. Towards the bottom, under Connections, hover over the text that says “+ Add connections”. In the text box that opens, type in the name "Demo" (or if you named it differently), and select the integration that was created in the last step; it may take a moment or two to appear. Once clicked, a popup window will show up asking us to confirm our selection. Click the red the Confirm button to finalize the connection.

Clicking the top right three dots on our database page to include our integration

 

At this point, all the permissions set for the integration apply to this Notion page and any child pages contained within it. However, one final piece of information is needed from Notion: the database ID. This ID is needed in order for Notion’s API to recognize where to look when accessing the database because multiple databases could be present on a single page.

To find the database ID, take a look at the URL of the page. If you are using the desktop app, right click on the Table tab of your database and click Copy link to view. Alternatively, in the search bar of your browser, the database ID is found between the last backslash and the first question mark.

If you are in a workspace, this is the format:

https://www.notion.so/{workspace_name}/{database_id}?v={view_id}

If not, this is the format: 

https://www.notion.so/{database_id}?v={view_id}

Save the database ID in the same place as your Notion API key. The next step is to set up the local project environment.

Project file setup and installation of the Notion SDK

First, create a project directory that will store the project files. Navigate to a place on your computer and create a new directory called notion-database. Then, change into the newly created directory.

The same result as above can be achieved with the following terminal commands.

mkdir notion-database 
cd notion-database

Once inside the empty notion-database directory in the terminal, run the following commands to initialize an npm project and install the Notion SDK. The last command installs dotenv, which is used to access the Notion API key and database ID in the .env file.

npm init -y
npm install @notionhq/client
npm install dotenv --save

Then, in the same directory, create the files index.js and .env with the following commands.

touch index.js
touch .env

If you are on Windows, creating these files directly in the IDE will work just fine.

Open the notion-database directory in your IDE. All the functions needed to work with the Notion database will be written in the index.js file, but first there is some necessary housekeeping.

All the files we have after installing all dependencies and creating the js, env, and gitignore files

As of now, these are the files in the project directory. The node_modules folder and the *.json files were created when the project was initialized via npm. Their purpose is to allow other Node dependencies to integrate with the project as seamlessly as possible.

Open the .env file and paste the following configuration, replacing <PLACEHOLDER> with your Notion API key and database ID, respectively.

NOTION_API_KEY=<PLACEHOLDER>
NOTION_DATABASE_ID=<PLACEHOLDER>

Save this file and then proceed to open the package.json file next. Add only one entry to the ”scripts” element so the program can run with the command npm run start in the IDE’s terminal.


{
  "name": "notion-database",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@notionhq/client": "^2.1.1",
    "dotenv": "^16.0.1"
  }
}

Copy the highlighted line and paste it directly below ”test” in the package.json file and save it.

Then as a final setup step, open up index.js and paste the following in the empty file.

const dotenv = require('dotenv');
const { Client } = require('@notionhq/client');
dotenv.config();

const notion = new Client({ auth: process.env.NOTION_API_KEY });
const databaseId = process.env.NOTION_DATABASE_ID;

The first two lines import Notion SDK Client and dotenv. The last two lines create a notion Client variable authorized by the API key found in the .env file and accessed using the dotenv library. Finally, a global databaseId variable is created and set to the other value stored in .env. These two variables will be used frequently throughout the code.

At this point, setup is complete and the rest of this tutorial will deal with manipulating data in the Notion database.

Insert data into a Notion database

Recall that the table accepts a title text ID, a rich text Name, a boolean Status, and a date Date which is accepted as a formatted string.

However, there are many other types of data available to use in a Notion database, which are known as properties, each with their own formats for adding, updating, and deleting.

After the imports and variable definitions, in the index.js file, paste the following asynchronous addToDatabase() function.

async function addToDatabase(databaseId, username, name, status, date) {
    try {
        const response = await notion.pages.create({
            parent: {
                database_id: databaseId,
            },
            properties: {
                'ID': {
                    type: 'title',
                    title: [
                    {
                        type: 'text',
                        text: {
                            content: username,
                        },
                    },
                    ],
                },
                'Name' : {
                        type: 'rich_text',
                        rich_text: [
                        {
                            type: 'text',
                            text: {
                                content: name,
                            },
                        }
                        ],
                },
                'Status': {
                    type: 'checkbox',
                    checkbox: status
                },
                'Date': { // Date is formatted as YYYY-MM-DD or null
                    type: 'date',
                    date: date
                },
            }    
        });
        console.log(response);
    } catch (error) {
        console.error(error.body);
    }
}

Test this function by calling it with the following test values. Paste this function call after its definition.

addToDatabase(databaseId, 'davidjones123', 'David Jones', false, null);

In the terminal in the project directory, or in the terminal session of your IDE, use the following command to run the code.

npm run start

If successful, a response will be printed to the console without errors. Afterward, check the Notion database to see that the entry has been successfully added.

New entry to notion database added using the notion API and SDK

Code explanation for adding data to database

The first thing this function does is wrap everything in a try catch block, which is necessary for debugging since any errors will be printed out to the console.

Next, a Notion page is created. In Notion databases, each row of the database is considered a page and has a unique page ID associated with it. In order for Notion to know that this page belongs in a database, it specifies its parent and sets the value to databaseId. Then, it proceeds with constructing a properties object, which is structured according to the schema of the database.

Each property is labeled the same as each column in the database, and each may have different data structures. For example, notice how the rich_text data type accepts an array of objects. This is due to the fact that Notion allows for a lot of customization of its rich_text object, and the array provides a structured way to define what sort of styles you would like your rich text to adopt.

When this page is created, Notion sends back a response. In this response, Notion will tell you via an HTTP 200 or 400 status code as to whether the action was successful. If it was not, the response will provide clues as to why. Typically the structure of a data type might not be formatted correctly in the object; the process does involve some amount of trial and error.

Query data from a Notion database

Notion provides some advanced filtering features when querying their databases. Any filter or sorting condition that can be used in their UI can also be used with their SDK.

In this tutorial, a unique username ID value will be queried to get the page ID for that specific entry. This page ID will be necessary for updating and deleting items in the database, which is demonstrated in the following sections.

Replace the call to addToDatabase() at the end of index.js with the following function definition.

async function queryDatabase(databaseId, username) {
    try {
        const response = await notion.databases.query({
            database_id: databaseId,
            "filter": {
                "property": "ID",
                "rich_text": {
                    "contains": username
                }
            }
          });  
        return response.results[0].id;
    } catch (error){
        console.log(error.body);
    }
}

Test this function with the following code. Paste it directly underneath the queryDatabase() function definition.

queryDatabase(databaseId, 'davidjones123')
    .then(result => {
        console.log(result);
    });

Save the file and run it with npm run start.

A code should print to the console composed of five segments of letters and numbers in an 8-4-4-4-12 character length pattern, separated by four dashes. This is the page ID for the davidjones123 database entry.

Confirm this page ID by going back to the database in Notion, right clicking the entry, and selecting Copy link.

Where to copy the link of a page in a notion database

When pasting this link to view it, it should look something like this: https://www.notion.so/davidjones123-7560067154ba42bebad1dfb548958fc3

Notice the code in the URL after the title davidjones123 and dash. It should be the same code as the one viewed in the terminal, minus the four dashes.

Code explanation for querying data from database

When querying a Notion database, the databases.query method is used and the databaseId is provided. If left as-is without specifying a filter, a response would be received of all the pages that are contained in the database. The number of pages shown in the response object is up to the page_size, which is a condition that may be included in the request, but is not covered in this tutorial.

Since the code does include a filter, it is set to look at the ID title property of the database and filter the results to find a page that contains a specified username, which in this case is davidjones123. The response from this call returns an object that contains, among other things, a nested list of objects called results, which in turn contains our results of the query.

In this case, a filter was specified for one unique entry, so the list that was received had just one item in it - the page that contains the David Jones entry. It is accessed at index 0, and the page ID property is extracted from the object in the list and returned. As long as the property being filtering for is unique, only one item in the results list should be received. Otherwise, the results list will contain multiple page objects to be iterated through.

Lastly, this returned value is printed with a then method because the response from Notion is a Promise. The Promise needs to resolve before the return value, stored in the result variable, can be accessed..

Update data in the Notion database

Now that query functionality is complete for the database, it’s time to explore how to update it. The queryDatabase() function will be used to retrieve the page ID of the entry of interest, and then that page ID will be used in a pages.update API call.

Replace the queryDatabase() call at the end of index.js and the attached .then() method with the following updateItem() function.

function updateItem(databaseId, username, status, inputDate) {    
    queryDatabase(databaseId, username)
        .then(async pageId => {
            try {
                const response = await notion.pages.update({
                    page_id: pageId,
                    properties: {
                        'Status': {
                            checkbox: status,
                        },
                        'Date': { 
                            type: 'date',
                            date: {
                                "start": inputDate
                            }
                        },
                    },
                    });
                console.log(response);
            } catch (error) {
                console.log(error.body);
            }
        });
}

The Status and Date properties are updated simultaneously for each entry. Test the function with the following code. Paste it just below the updateItem() definition.

updateItem(databaseId, 'davidjones123', true, "2022-08-19");

After running the above with npm run start, navigate back to the Notion database to view the changes.

The notion database with updated checkbox and date values

The table has been successfully updated, with the checking the previously unchecked box and adding a date to our empty date cell!

Code explanation for updating data in database

Notice how similar the function call to queryDatabase() is to how it was called when it was tested, with the exception of the added async keyword included in the arrow function. async is used here because the API call is asynchronous, and since the query Promise needs to resolve in order to obtain the pageId, the pages.update() is put in the .then method.

This pageId returned from queryDatabase() is provided to the pages.update() API call instead of the databaseId that we used previously. The properties is updated by setting the Status checkbox to true and the Date to a start date of the inputDate argument. The response from the API call is then logged to the screen to indicate success or failure.

Delete data from our Notion database

The basic functionality required to use a Notion database is almost complete! The last step is to learn how to delete an entry. Luckily, this process is almost exactly the same as updating, but the code is even simpler.

Replace the call to updateItem() at the end of index.js with the following deleteItem() definition.

function deleteItem(databaseId, username) {
    try {
        queryDatabase(databaseId, username)
            .then(async pageId => {

                const response = await notion.blocks.delete({
                    block_id: pageId,
                });
                console.log(response);

            })
    } catch (error) {
        console.log(error.body);
    }
}

Test this code by pasting the following function call right below the deleteItem() definition.

deleteItem(databaseId, 'davidjones123');

Run this with npm run start and navigate back to your database, which should now display Empty table instead of the davidjones123 entry.

Deleting our entry in the database and showing an empty table

As seen in the above screenshot, the entry was successfully deleted!

Code explanation for deleting data from database

The deleteItem() function works in a very similar way to updateItem(). The difference is that it is only necessary to provide the pageId to the blocks.delete call instead of providing a complete set of data that is used in updating.

Actually, there is one subtle difference, and that is using blocks instead of pages, and providing the pageId where it asks for a blockId. Thankfully, these two different IDs can be used interchangeably in most cases since a Page is a type of Block.

Blocks are a broad category of content in Notion, and deleting them will archive them. In the event of an accidental deletion, the Block can be restored by updating them similar to how we updated a Page earlier.

After deleting the Block, the response is printed to ensure our action was a success.

Now you know how to manipulate data in a Notion database!

With the knowledge you have gained thus far, you can now harness the power of Notion databases for all manner of small scale web development projects or prototypical applications. You can effectively keep state in serverless environments and manipulate all kinds of data simply through API calls. With it, you are confined only to the limits of your imagination!

Be sure to view this companion repository for a complete view of the code used throughout this tutorial, including the ordered function calls at the bottom, just in case you came across any bottlenecks and would like to see it all put together.

Hayden Powers is a Developer Voices intern on Twilio’s Developer Network team. She enjoys enthusing over creative ideas and endeavors, and hopes you will reach out to her with yours at hpowers[at]twilio.com or on LinkedIn.