DEV Community

Cover image for How to create a form app with the Notion API
Ryan
Ryan

Posted on • Originally published at commotion.page

How to create a form app with the Notion API

A tutorial using React.js and Express.js

The Notion API officially came out of beta on May 13, 2021, enabling web developers to create cool apps that integrate with your Notion workspace, an example being ourselves, Commotion. For our form tool, we use the Notion API to save form responses to a connected Notion database. In this tutorial, we’re gonna show you how to create a simple form tool like ours that accepts emails with an html form and saves it to your Notion workspace. Let’s get started!

1. Setup the Notion stuff

First, create a Notion account (obviously). Then create a Database table (typing in ‘/database’) and add a column called Email that is of type email, like below.

Sample Notion Database

Open the database object as its own page by clicking the expand icon. We’ll come back to this in a second.

Expand Notion Database

Then go to https://www.notion.so/my-integrations and select “Create new integration” and give it the name “Basic Form” or something.

Notion integrations page

Go back to your database’s Notion page and share the database with the app you just created by hitting Share on the top right, clicking the Invite input in the dropdown, and selecting your app from the popup. This makes sure that the API actually has access to this database.

Notion share with API

2. Setup your backend with Express.js

For this tutorial, we need to setup a backend, so we’re going with Express. You can read how to set it up here, but the gist of it is to go to a new folder and input the following commands in your terminal.

$ mkdir form-tool-backend
$ cd form-tool-backend
$ npm install express
$ npm install axios
$ npm install cors
Enter fullscreen mode Exit fullscreen mode

Within the form-tool-backend folder, create a file app.js and paste the following into it just to get things going.

// form-tool-backend/app.js

const express = require('express')
const axios = require('axios')
const app = express()
const port = 3002

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

Why do we need a backend? Because we need to use secret keys that we don’t want to leak out. If someone were to get your secret key, that would be able to read and and write anything to your Notion workspace! Very scary.

3. Retrieve Database Information with the Notion API

Finally, let’s actually start building! I’d open up https://developers.notion.com/reference cause we’ll be referencing this page a lot. First, we need to get our secret key, so let’s go back to https://www.notion.so/my-integrations, select our app, then copy our Internal Integration Token. Make sure to not leak this!

Get secret key

Paste the following code below into your app.js and replace the secretKey variable with the token you just copied. We’ll then create a route that fetches the database data with axios.

// form-tool-backend/app.js

const express = require('express')
const axios = require('axios')
const app = express()
const port = 3002

// REPLACE! Retrieved from https://www.notion.so/my-integrations
const secretKey = 'secret_***************'

// What we'll pass into axios
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${secretKey}`,
  'Notion-Version': '2021-08-16',
}

// Route to fetch database data
app.get('/:database_id', async (req, res) => {
  const { database_id } = req.params;
  const resp = await axios({
    method: 'GET',
    url: 'https://api.notion.com/v1/databases/' + database_id,
    headers
  });
  return res.json(resp.data);
})

app.use(cors())
app.use(express.json())
app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})
Enter fullscreen mode Exit fullscreen mode

Now we have something to test! Within your form-tool-backend folder, run:

$ node app.js
Enter fullscreen mode Exit fullscreen mode

Go back to your Notion database in your browser and copy your database_id from your Notion url, marked as the part here:

Get Database ID

Now if your run the following in a different terminal tab:

curl http://localhost:3002/REPLACE_THIS_WITH_DATABASE_ID
Enter fullscreen mode Exit fullscreen mode

You should get the data from your database!

4. Setup your frontend with React.js

Next, we need to create our frontend. In this case, we’re using React. Go to your terminal, go to the folder you want to put all your code in, and input the following command to create a sample app.

$ npx create-react-app form-tool
$ cd form-tool
$ npm start
Enter fullscreen mode Exit fullscreen mode

Empty React app

Let’s make some changes, shall we? Replace the contents of the App.js with the below, and replace the database_id value with the one you copied earlier from the Notion URL.

// form-tool/src/App.js

import { useEffect, useState } from 'react';

const database_id = REPLACE_WITH_YOUR_DATABASE_ID;

function App() {
  const [db, setDB] = useState({});

  useEffect(() => {
    // We fetch the data from the local server that we're running
    fetch('http://localhost:3002/' + database_id).then(async (resp) => {
      setDB(await resp.json())
    });
  }, []);

  return (
    <div>
      {db.title?.[0]?.plain_text}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

This just gets the data that our backend retrieves and brings it into our frontend. The schema for a database object is a little unique, so checkout the official documentation here. If we run npm start, we should get the following.

Bare bones site

We’ve officially fetched data from our Notion workspace! Hooray!

5. Save form response to Notion

Finally, what we’ve all been waiting for, let’s actually take some information and save it to our database shall we? We’ll start by adding a simple form in our frontend React project that sends the email + name to our backend to send to Notion.

import { useEffect, useState } from 'react';

const database_id = REPLACE_WITH_YOUR_DATABASE_ID;

function App() {
  const [db, setDB] = useState({});

  // NEW! This will send the form information to our backend we setup
  const onSubmit = (e) => {
    e.preventDefault();
    fetch('http://localhost:3002/' + database_id, {
      method: 'POST',
      body: JSON.stringify({ email: e.target.email.value, name: e.target.name.value }),
      headers: { "Content-Type": 'application/json' }
    });
  }

  useEffect(() => {
    fetch('http://localhost:3002/' + database_id).then(async (resp) => {
      setDB(await resp.json());
    });
  }, []);

  return (
    <div>
      {db.title?.[0]?.plain_text}
      {/* NEW! This is an extremely simple form to collect the information */}
      <form onSubmit={onSubmit}>
        <input name="name" placeholder="Name" required />
        <input name="email" placeholder="Email" type="email" required />
        <button>Submit</button>
      </form>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Add simple form

We now have an extremely simple form. Right now, nothing actually happens if we submit the form. That’s cause we need to build the backend function! If we switch back to our Express app, we can create a POST route that accepts our data, the name and email, and sends it to our Notion database. Entries in a database are equivalent to a Notion “page”, so we need to create a page where the database_id is the parent. You can reference https://developers.notion.com/reference/post-page to get a better sense of what this means.

const express = require('express')
const axios = require('axios')
const cors = require('cors')
const app = express()
const port = 3002

// REPLACE! Retrieved from https://www.notion.so/my-integrations
const secretKey = 'secret_***************'

// What we'll pass into axios
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${secretKey}`,
  'Notion-Version': '2021-08-16',
}

// NEW! This is a new route that will actually send the information to Notion
app.post('/:database_id', async (req, res) => {
  const { database_id } = req.params
  const { name, email } = req.body
  await axios({
    method: 'POST',
    url: 'https://api.notion.com/v1/pages',
    headers,
    data: {
      parent: { database_id },
      properties: {
        "Name": { "title": [{ text: { content: name } }] },
        "Email": { "email": email }
      }
    }
  })
})

// Route to fetch 
app.get('/:database_id', async (req, res) => {
  const { database_id } = req.params;
  const resp = await axios({
    method: 'GET',
    url: 'https://api.notion.com/v1/databases/' + database_id,
    headers
  });
  return res.json(resp.data)
})

app.use(cors())
app.use(express.json())
app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Enter fullscreen mode Exit fullscreen mode

Once we get this up and running and we restart our backend by quitting and rerunning node app.js , we can input a name and email into our form and it will save to our Notion database!

Saved database

6. Bonus: For next time

So this has been a tutorial on how to save form responses to YOUR Notion database, but this doesn’t explain how we at Commotion are able to save responses to ANYONE’s Notion database. This requires making your integration a public one and having your user connect their Notion account like how we do it.

Connect with Notion

Notion connector

We’ll save this for another time, but it’s not actually too bad. We’re 75% of the way there already. In the mean time, please check out Commotion if you need to create forms that work with your Notion workspace or want to send emails to contacts in a Notion database, mail merge style. We hope that this was informative!

Top comments (1)

Collapse
 
llbbl profile image
Logan Lindquist

This is great Ryan. Would love to pick your brain on Notion stuff.