Using Notion API with JavaScript
Tags: notion, api, javascript
Published: 2022-10-08 22:43:37

---
title: Using Notion API with JavaScript
published: true
description: Learn how to use the Notion API with JavaScript
tags: notion, API, javascript
canonical_url: https://awesomecoding.co/posts/using-notion-api-with-javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6aiv39n7qo0b0xdpv581.jpg
---

Notion is an awesome organizational tool I have been using for a long time. It has so many capabilities that it slowly replaced many other tools I use daily. One of the ways that I use Notion is to create content for my [personal website](https://www.enginarslan.com/).

My personal website is built as a **static website**. This means that I have to provide all the information I want inside the website before deploying it on the web. Once it is deployed, it doesn’t change. This means I must redeploy the website anytime I want to update it.

This might sound cumbersome, but it isn’t. Building, updating and deploying a website means a couple more steps are involved, but it doesn’t take much more time. Additionally, having a static website has some benefits in terms of **performance**, **cost-effectiveness** and **resilience**. A static website is displayed quickly from anywhere worldwide, can be hosted freely and wouldn’t crash under heavy traffic. It happens to be a great architectural decision for certain kinds of websites.

There is a page on my website listing the [books I like](https://www.enginarslan.com/library). Maintaining this kind of long and dynamic list using **HTML** can be cumbersome. You would probably need a visual editor accessible from anywhere to edit that content easily.

![https://i.imgur.com/efLftNW.gif](https://i.imgur.com/efLftNW.gif)

That’s why I have a **database** in Notion that I could easily populate with book information and import into my website. You can see what that database looks like [here](https://cuboid-hoof-835.notion.site/7c579be494fc4f87bc827c6653f52dfd?v=b03c21ff315740c8a951819476a82526). Before discovering the **Notion API**, I used to export this database as a **CSV** and parse that CSV to populate the books list page. However, this process included too many steps, so I avoided doing it frequently.

However, Notion API makes this process super simple. In this post, I will show how I interact with the **Notion API** using **JavaScript** to populate my application with data from my Notion account.

## Integrating with the Notion API

You can find the repository that includes the code from this post in my [github repo](https://github.com/hibernationTheory/awesome-coding-using-notion-api-with-javascript).

This post assumes that you have a Notion account. Go to the [https://www.notion.so/my-integrations](https://www.notion.so/my-integrations) page to create an **API integration** with Notion. An API integration essentially creates a way for you programmatically interact with a resource on Notion. That resource could be a page or a database. In this example, we will look into interacting with a **database**, but I will also have another post showing how to interact with **pages**.

> We will create an **internal integration** in **our workspace** using **read content capability.**

Create an **internal** **integration** by giving it a name and some access capabilities. At a minimum, we need a **read-content** capability. Since we will be creating an API integration for our own usage, the capabilities we give to this app can be as generous as you would like. However, if we were to create a public integration, we would want to be more careful when deciding which access capabilities we might need. Too many and unneeded capabilities might make our users uneasy.

For this project, I created an integration called `Book Reviews`, but you can call yours whatever you want.

![https://i.imgur.com/Zt7U5tx.png](https://i.imgur.com/Zt7U5tx.png)

Create the integration and take note of the **Internal Integration Token** that is generated. This is a secret token that will allow us to control this integration. Now that we created the integration, go to a page in Notion where you would like to have this integration functional. For the sake of this example, choose a page that contains a full-page database. Then, click **Add Connections** to connect the integration to the page. If you don’t have a page available, you can duplicate mine: [https://cuboid-hoof-835.notion.site/7c579be494fc4f87bc827c6653f52dfd?v=b03c21ff315740c8a951819476a82526](https://cuboid-hoof-835.notion.site/7c579be494fc4f87bc827c6653f52dfd?v=b03c21ff315740c8a951819476a82526)

![https://i.imgur.com/kZcfyng.png](https://i.imgur.com/kZcfyng.png)

We need to note the ID of this resource (page/database), so we can refer to it in our code. You can find the ID in the URL. Here is what the URL looks like in my case `https://www.notion.so/7c579be494fc4f87bc827c6653f52dfd?v=b03c21ff315740c8a951819476a82526`. The ID is the part that is before `?` and after the domain name, which is `7c579be494fc4f87bc827c6653f52dfd`.

## Fetching Data from Notion API

We can connect to the Notion API just like how you would connect to any API. For example, we can use the command line **curl** tool. However, Notion has a **JavaScript client library** that makes interacting with this library much easier. We will be using it in our example. I will be assuming you have **Node.js** and **npm** installed on your system.

Let’s create a new project by creating a folder and then creating a `package.json` file inside that folder.

```bash
mkdir book-reviews
```

We will run `npm init -y` to initialize an empty `package.json` inside this folder.

```bash
npm init -y
```

Now that we have a `package.json` file, we can start installing the packages that we will be using in this project.

```bash
npm install --save @notionhq/client@^2.2.1
npm install --save dotenv@^16.0.3
```

`@notionhq/client` is the library that will help us use the Notion API easier. `dotenv` is a library that makes it easier to use the secrets we store in environment variables. What secrets, you might ask. Remember the **Internal Integration Token** generated when we first created our integration? That will be the secret key we will use to communicate with the API. Let’s create a file called `.env` to store that secret and the database id.

```
NOTION_KEY=<Paste Your Internal Integration Token here>
NOTION_DATABASE_ID=<Pase your Notion Database ID here>
```

`.env` files are a way to manage secrets in application development. They are not committed to **source control** or anywhere that is public. To prevent it from going to git-based source control, you can ignore it using a `.gitignore` file alongside other things you might want to ignore, like `node_modules`.

```
.env
node_modules
```

We can now start writing our JavaScript logic to fetch the book list data from Notion! Create a file called `index.js`.

```jsx
const dotenv = require('dotenv')

dotenv.config()

console.log(process.env.NOTION_KEY)
console.log(process.env.NOTION_DATABASE_ID)
```

Here we are importing the `dotenv` library and running the `config` method on it. This should load the variables stored inside the `.env` file into the environment. We are using `console.log` to print those variables to the screen to ensure they are working as expected.

We now have the pieces we need to start interacting with the Notion API and the database.

```jsx
const Client = require('@notionhq/client').Client
const dotenv = require('dotenv')

dotenv.config()

const NOTION_CLIENT = new Client({ auth: process.env.NOTION_KEY })
const DATABASE_ID = process.env.NOTION_DATABASE_ID

async function getDatabaseData(client, databaseId) {
  const response = await client.databases.query({
    database_id: databaseId,
  })

  console.log(response)
}

getDatabaseData(NOTION_CLIENT, DATABASE_ID)
```

Here I am creating two **constant variables** used throughout the file: `NOTION` and `DATABASE_ID`.

We are also creating an `async` function called `getDatabaseData` that asynchronously connects to the given `database_id` using the **Notion Client** we instantiated. It takes the Notion Client library and the database id as arguments. It fetches the results and logs them to the screen.

```jsx
async function getDatabaseData(client, databaseId) {
  const response = await client.databases.query({
    database_id: databaseId,
  })

  console.log(response)
}
```

Including some **error handling** is a good idea since many things can go wrong when working with a remote API. We will handle the errors using a **try catch** statement.

```jsx
async function getDatabaseData(client, databaseId) {
  try {
    const response = await client.databases.query({
      database_id: databaseId,
    })

    console.log(response)
  } catch (error) {
    console.error(error)
  }
}
```

The database you are working with will be different than mine, but you might notice these values at the end of the data structure you receive. Note the `has_more` property.

```jsx
next_cursor: '54bf7ed3-b876-45cc-b3ad-01eb8df0cb56',
has_more: true,
type: 'page',
page: {}
```

`has_more` being `true` means that the response you are receiving from the API is **paginated**. There is more data available than what we are currently receiving. The rest of the data is on the following pages**.** The Notion API returns only 100 results per query by default. The query results are contained inside `response.results` array. We will save the query results inside an array called `results` and log the length of that array (which will be logging a number that is 100 or less).

```jsx
async function getDatabaseData(client, databaseId) {
  try {
    let results = []

    const response = await client.databases.query({
      database_id: databaseId,
    })

    results = [...results, ...response.results]
    console.log(results.length)
  } catch (error) {
    console.error(error)
  }
}
```

We will also update our code to get the results contained in the following pages.

```jsx
async function getDatabaseData(client, databaseId) {
  try {
    let results = []

    const response = await client.databases.query({
      database_id: databaseId,
    })
    results = [...results, ...response.results]

    // while loop variables
    let hasMore = response.has_more
    let nextCursor = response.next_cursor

    // keep fetching while there are more results
    while (hasMore) {
      const response = await client.databases.query({
        database_id: databaseId,
        start_cursor: nextCursor,
      })
      results = [...results, ...response.results]
      hasMore = response.has_more
      nextCursor = response.next_cursor
    }

    console.log(results.length)
  } catch (error) {
    console.error(error)
  }
}
```

To fetch the next page, we need to provide a `start_cursor` value to the database query. `start_cursor` indicates the location in the list where we should be fetching the results. We are making it so that the `start_cursor` for the next fetch would equal the `next_cursor` of the current fetch results.

```jsx
const response = await client.databases.query({
  database_id: databaseId,
  start_cursor: nextCursor,
})
```

We are doing this inside a **while loop** that will run until `has_more` is equal to false.

```jsx
// while loop variables
let hasMore = response.has_more
let nextCursor = response.next_cursor

// keep fetching while there are more results
while (hasMore) {
  const response = await client.databases.query({
    database_id: databaseId,
    start_cursor: nextCursor,
  })
  results = [...results, ...response.results]
  hasMore = response.has_more
  nextCursor = response.next_cursor
}
```

With this logic in place, we should be able to fetch all the database data from Notion. Next, let’s format this data to use for our purposes.

## Formatting the Data From Notion API

We will update the `getDatabaseData` function to return the `results` array instead of just logging it. We will use this function inside another function called `main`.

```jsx
const Client = require('@notionhq/client').Client
const dotenv = require('dotenv')

dotenv.config()

const NOTION_CLIENT = new Client({ auth: process.env.NOTION_KEY })
const DATABASE_ID = process.env.NOTION_DATABASE_ID

async function getDatabaseData(client, databaseId) {
  try {
    let results = []

    const response = await client.databases.query({
      database_id: databaseId,
    })
    results = [...results, ...response.results]

    // while loop variables
    let hasMore = response.has_more
    let nextCursor = response.next_cursor

    // keep fetching while there are more results
    while (hasMore) {
      const response = await client.databases.query({
        database_id: databaseId,
        start_cursor: nextCursor,
      })
      results = [...results, ...response.results]
      hasMore = response.has_more
      nextCursor = response.next_cursor
    }

    return results
  } catch (error) {
    console.error(error)
  }
}

async function main() {
  const data = await getDatabaseData(NOTION_CLIENT, DATABASE_ID)

  console.log(data)
}

main()
```

We have simply added a `return results` statement inside the `getDatabaseData` function. We have additionally created the `main` function where we call the `getDatabaseData` function.

```jsx
async function main() {
  const data = await getDatabaseData(NOTION_CLIENT, DATABASE_ID)

  console.log(data)
}
```

If we look at the received data, we will notice that the properties of the data we are most likely interested in are under the `properties` key.

```jsx
properties: {
 review: [Object],
  url: [Object],
  quote: [Object],
  rating: [Object],
  priority: [Object],
  title: [Object]
},
```

These properties correspond to the **columns** of our Notion database. Let’s console.log them using `JSON.stringify` to see what they look like. Currently, we can only see that they are JavaScript objects (`[Object]`). Using `JSON.stringify` would help us investigate the internals of this structure.

```jsx
async function main() {
  const data = await getDatabaseData(NOTION_CLIENT, DATABASE_ID)
  const firstDataItem = data[0]

  console.log(JSON.stringify(firstDataItem, null, 2))
}
```

You will notice that we are getting objects with many properties representing the data stored inside our database columns. Each piece of data contains meta information like `id`, `type` and `rich_text`. The `rich_text` property is particularly interesting, representing a text as an array of objects. The array might have more than one element if you use a different kind of styling for the words inside a text like **bold**, _italic_ or `code`. My database entries are just using plain text, so I will not worry about this.

The only thing left to do for us here is to format the received Notion API data into something easier to consume. I will create a function called `normalizeDataItem` that would format a given data item.

```jsx
function normalizeDataItem(item) {
  const { url, review, quote, rating, priority, title } = item.properties

  return {
    url: url.url,
    review: review.rich_text[0]?.plain_text ?? '',
    quote: quote.rich_text[0]?.plain_text ?? '',
    rating: rating.number,
    priority: priority.number,
    title: title.title[0]?.plain_text ?? '',
  }
}
```

We can run our results through this `normalizeDataItem` function to get a more usable data structure.

```jsx
async function main() {
  const data = await getDatabaseData(NOTION_CLIENT, DATABASE_ID)
  const normalizedData = data.map((item) => normalizeDataItem(item))

  console.log(normalizedData)
}
```

We are pretty much done! It is probably not super fruitful to just `console.log` results. Let’s update our code to write the results into a JSON file. We will first import two built-in node libraries called `path` and `fs` at the top of our file.

```jsx
const fs = require('fs')
const path = require('path')
```

We will then update our `main` function to write the returned results to a JSON file on disk.

```jsx
async function main() {
  const data = await getDatabaseData(NOTION_CLIENT, DATABASE_ID)
  const normalizedData = data.map((item) => normalizeDataItem(item))

  const outputFilePath = path.join(__dirname, 'data.json')
  fs.writeFileSync(outputFilePath, JSON.stringify(normalizedData, null, 2))
}
```

And that’s pretty much it! You can now do whatever you want with this JSON file. In my case, I feed it into my **Next.js** app in build time to populate the website with the content from the data.

I hope you enjoyed this walkthrough. Formatting the page data from Notion is a bit more tricky. I will explore this in a separate blog post. Meanwhile, feel free to follow me up at: [https://twitter.com/inspiratory](https://twitter.com/inspiratory) and [https://www.linkedin.com/in/enginarslan/](https://www.linkedin.com/in/enginarslan/).