Notion as a Database

Notion as a Database

How I'm using Notion as a database to power the content for my portfolio website.

(Updated Apr 11, 2024)

I started this website as a playground to experiment with new things I learn along my development journey. When I learn something new, I first try to use it with a feature on here.

This website was the first project I migrated to Next.js 13 when it was first released, the first project I used the app router on while it was in beta, and the first I tried server actions on — you get the flow, it's very experimental.

I started with the idea of not taking it too seriously as a production website, and I also didn't want to spend much on making it. I had two choices:

  1. Write a simple Next.js app, use CSS for styling, and use the free tier of any database provider at the time to serve the content I needed to store somewhere, e.g., a blog.
  2. Write a simple Next.js app and see how I can use Notion for all the pages.

I went with option 2. The first one seemed too traditional. The main reason I went with 2 is I noticed Notion had this concept of pages that you could publish to the web and have something like your http://yourname.notion.so, which was pretty cool, apart from the part where it was just a click of a button, with nothing to learn from that. I wanted to try to replicate how they do it.😐.

So I set out to look at network requests and cookies from the Notion page I had published, and I noticed they were reading something called a token_v2 and notion_user_id, which were used in requests to fetch the blocks and convert them to what they render on the page.

I tried a request with those values, and it went through. I went ahead to write a renderer for the blocks, which is basically a really long switch statement. Here's a snippet from it:

switch (block.type) {
    case 'page':
    ...
    handling a full page
    ...
    case 'inner_header_block':
    ...
    handling header blocks like h1, h2..
    ...
    case 'divider':
        return <hr className='notion-hr'/>
    case 'numbered_list':
    ...
    handling numbered lists
    ...
}
    

This goes on and on to handle all the types of blocks Notion supported, except for file blocks, which were not something I had a specific use for.

At the end, I could build pages in Notion, then my Next.js app would fetch these pages at build time and generate static pages out of them to reduce load time later on and so I don't abuse my access to Notion.

2024-04-11_12.20.32.jpg

Notion's Official Developer API release

In May 2021, Notion released their developer API in beta. I had mixed feelings about it. I remembered this quote from one of my favourites;

"Any application that can be written in JavaScript will eventually be written in JavaScript." — Jeff Atwood

Even though my approach worked, I felt it wasn't good because copying values from cookies and network requests to make a call to a protected endpoint looked like undermining the platform APIs. So Notion made that part of making requests in my code obsolete, and the rendering also might need adjustments for the new approach using the official API.

I left the website running for a while as I was exploring the new API and trying it out. It was quite simple to use:

npm install @notionhq/client
  • Create an internal integration in notion from here.

    source: notion integrations page

    • Then get your API token from the secrets tab of the integration page.
  • In your code, you can then import the client and pass your API token and the page you want to interact with, like this:

import { Client } from "@notionhq/client"
import { config } from "dotenv"

config()

const pageId = process.env.NOTION_PAGE_ID
const apiKey = process.env.NOTION_API_KEY

const notion = new Client({ auth: apiKey })
  • If you're wondering where to get pageId, you can open up your Notion page in the browser, and in the address bar, you would find a link like below. The pageId is the part before the question mark (?) and after www.notion.so/aikins/. So, 253536cb0fe0485114627b67c16c714b.
https://www.notion.so/aikins/253536cb0fe0485114627b67c16c714b?v=cf21fc38dec1267b8f7f868610261d19
  • Then in your page settings under integrations, allow your integration access to the page.
  • After doing these, you should be able to do things like add blocks to the page, get blocks from the page, and more.

I then decided to make adjustments to my website with this approach.

Redoing my website

I started checking out how portfolios have been looking lately from a few portfolio websites and some Dribbble shots. I then came up with a small Figma design, having in mind a few rules I took from the notadesigner newsletter from the incredible Saron Yitbarek and the laws of UX design.

Instead of getting a full page from Notion, I can use my existing blog database, which was already a store for my posts, then I created another database to store content from my wall too.

2024-04-11_12.20.20.jpg

My new approach was to fetch the posts from the Notion database, convert the Notion blocks to Markdown front matter, which is a really tried-and-true way of building blogs, then I use Contentlayer to serve the converted Markdown files to JavaScript to be rendered on the page statically. 🔥. All of this is done at build time to generate the static pages, and the result is a blazing-fast blog that you're currently reading.

For my wall, I call the Notion API to pull and push signatures to the wall database. Putting all of this architecture together, the result is this website that you're reading on.

In conclusion

The Notion team has done a commendable job by building this tool that almost everyone is using, from note-taking to task management, to content planning — it was infinite. And they pushed the limit by allowing users who have coding knowledge to be able to interact with these programmatically! It's just beautiful, and I thank them. I also thank you, my dear reader, for getting to the end. I promise to bring better and better stuff.

I'm Aikins — I love to build stuff.