Zero
Zero
Back

Using Notion as a Human-Readable Database

Capture form submissions from your web app and store them where your team works.

Sam Magura

Sam Magura

Old-school mailboxes at a post office

When developing an application, data is usually stored in a relational database or a NoSQL database. While these databases are extremely powerful, they aren't easy to use for non-technical users, and they are overkill for simple tasks. In this post, we'll build an application that uploads form submissions to a Notion  database. If you aren't familiar with it, Notion is essentially a wiki platform with many advanced features.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Secure your secrets conveniently

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.

Zero dashboard

What We're Building

For this example project, we'll build a lead capture form where a prospective buyer can leave their email address and name. Later, a salesperson would reach out to the user to provide them additional information about the product.

The web application will be built on Next.js. The goal is to upload the form submissions (name and email) to a Notion database, with each form submission becoming a row in the database. Your organization's salespeople would reference the Notion database to figure out who to contact.

To get the data into Notion, we'll use the Notion API  through the official Notion JavaScript SDK . The Notion API key will be retrieved from the cloud at runtime with the help of the Zero secrets manager.

Creating a Notion Database

If you don't already have a Notion account, you can sign up for one for free at https://notion.so . The first step is to create a Notion database . To do this, you'll create a blank Notion page and then select "Table" at the bottom. Notion's terminology is inconsistent in this area — a table and a database are really the same thing.

You'll now be able to customize the columns of the database. Our database will have just two columns, Name (type = text) and Email (type = email). The rows of the database should be left blank, since those will be filled in by our application.

Creating a Notion Integration

To work with the Notion API, we'll need to create an internal Notion integration, as described on this docs page . An internal integration just means the integration is used inside your organization, not by outside users.

You can create the integration on the integration settings page . You really just need to enter a name for the integration, e.g. Lead Form, and select the Notion workspace to connect it to.

Creating an internal Notion integration
Creating an internal Notion integration

Once the integration is created, you'll be presented with the integration's secret key (API key). Please copy this key into a text file for now.

Not only does the integration need to be connected to your workspace, you also have to grant the integration access to the specific pages it should be able to modify. In our case, the integration should be able to modify the database and all its subpages. So navigate to the database page, and grant the integration access to it using the menu item shown here .

Importing the API Key into Zero

Since our app will be pulling the secret from Zero at runtime, we need to import the Notion API key into Zero. Log in to Zero and create a new project. Create a token for the project and save it in that same text file. The application will exchange this token for the secrets when it calls the Zero API.

Once the project is created, add a new secret. For the name, enter "Notion", and for the fields, write API_KEY and paste in the Notion API key.

Building the Next.js App

Now, let's start coding. To bootstrap a Next.js app, run

Terminal
npx create-next-app@latest notion-app

and select TypeScript.

The goal is to create a lead capture form that looks like this:

The lead capture form in Next.js
The lead capture form in Next.js

When the form is submitted, it should call a backend API method (a Route Handler, in Next.js terminology). I'll skip over the code for creating the form. As always, you can find the full code in the zerosecrets/examples  GitHub repository.

Fetching the Notion API Key

Before we code up our Route Handler, we'll need to pull down the Notion API key from Zero. First, install the Zero TypeScript SDK and the Notion SDK:

Terminal
npm install @zerosecrets/zero @notionhq/client

Now, we'll use the standard pattern for fetching secrets from Zero, which is used in almost all of our blog posts:

util/getNotionClient.ts
import {Client} from '@notionhq/client' import {zero} from '@zerosecrets/zero' let client: Client | undefined export async function getNotionClient() { if (client) { return client } if (!process.env.ZERO_TOKEN) { throw new Error('Did you forget to set the ZERO_TOKEN environment variable?') } const secrets = await zero({ token: process.env.ZERO_TOKEN, pick: ['notion'], }).fetch() if (!secrets.notion) { throw new Error('Did not receive Notion secret.') } client = new Client({auth: secrets.notion.api_key}) return client }

The getNotionClient function can now be called from any backend API method that needs to access Notion.

Coding the API Method

The API method POST api/createLead is quite simple actually. It just needs to extract the name and email from the request body, and then call the Notion API to insert a new page into the database. First, scaffold the Route Handler like this:

app/api/createLead/route.ts
export async function POST(request: Request) { const body = await request.json() const notion = await getNotionClient() // TODO Call Notion API return Response.json(null) }

To call the Notion API, you'll need to locate your database ID, which is documented here . We'll utilize the notion.pages.create API method, since each row in the Notion database is actually a page. It took me a while to figure out the proper syntax for the properties, but fortunately, the Notion SDK provides TypeScript Intellisense that can help you figure it out. Here's the final code, which should be incorporated into the Route Handler skeleton shown above:

app/api/createLead/route.ts
const notionResponse = await notion.pages.create({ parent: { // TODO Put your database ID here database_id: '147c283ff3318039867efce5512f6173', }, properties: { Name: { type: 'title', title: [ { type: 'text', text: { content: body.name, }, }, ], }, Email: { type: 'email', email: body.email, }, }, }) console.log(`Created Notion page: ${notionResponse.id}`)

Testing it Out

To test your app, you'll need to provide your project's Zero token as an environment variable:

Terminal
ZERO_TOKEN='YOUR_ZERO_TOKEN' npm run dev

Fill out the lead capture form with any name and email and click Submit. If everything worked, you should see a record in your Notion database!

The Notion database showing the leads that were entered via the form
The Notion database showing the leads that were entered via the form

In Conclusion

This post showed that you don't always have to use a full-featured database like SQL Server or PostgreSQL to capture data from a web application. Using Notion as a database makes the data completely human-readable. Plus, you can add additional columns and data to the Notion database with ease. An alternative to Notion in this scenario would be to send a Slack message each time the form is submitted. There are pros and cons to each approach, so it's really up to you what you choose.


Other articles

A bucket

Managing Files in Amazon S3 from Node.js

Adding and removing files from S3 is a breeze with the AWS JavaScript SDK.

A pattern of window shades

Monitor a Node.js App with Datadog and Winston

Quickly set up error logging in SvelteKit or any another Node.js-based framework.

Secure your secrets

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.