A library to simplify adding and querying Notion databases/tables. Giving typeahead/intellisense support on columns and expected column values on user specified databases. Built on top of Notion API
Databases with the following column types are supported:
- Multi-select
- Select
- Status
- Date
- Text
- Url
- Checkbox
- Phone Number
The only requirement is a Notion Developer API key (here) and database IDs you want. Be sure to connect your integration (🚧 Permissions section) with your tables
npm install @haustle/notion-orm --save-dev
At the root of your project create a notion.config.js
file
You’ll need to pass your developer key and database IDs. How to get database IDs here
// notion.config.js
const auth = process.env.NOTION_KEY;
const NotionConfig = {
auth,
databaseIds: [
"a52239e4839d4a3a8f4875376cfbfb02",
"5f4bf76a1e3f48d684d2506ea2690d64",
],
};
module.exports = NotionConfig;
Execute the following command from the root project directory.
npx notion generate
Package Size
Unpackaged size is 70.6KB and the installation size is 5.12MB (5.03MB from @notionhq/client
dependency)
Databases can be imported via barrel file or from the individual database file. All database names will be camelCase 🐫.
// Barrel Import (access to all databases)
import * as notion from "@haustle/notion-orm";
notion.databaseName.add();
notion.databaseName2.query();
// Individual Database Import
import {
databaseName,
DatabaseSchemaType,
QuerySchemaType,
} from "@haustle/notion-orm/build/db/databaseName";
databaseName.add();
DatabaseSchemaType
: Object type accepted in the database’sadd()
functionQuerySchemaType
: Object type accepted in the database’squery()
function
The following examples for querying & adding are for server-side calls. If you’re looking to use this framework to execute client-side calls (ex. button click add/query X) visit the Client (React) section after reading
Adding
Only required column required is the title.
notion.books.add({
bookName: "Raphael, Painter in Rome: a Novel", // title
author: "Stephanie Storey", // text
status: "In progress", // status
numberOfPages: 307, // number
genre: ["Historical Fiction"], // multi-select
rating: "⭐️⭐️⭐️⭐️", // select
startDate: {
// date
start: "2023-01-01",
},
phone: "0000000000", // phone
email: "[email protected]", // email
});
All column types in Notion databases are mapped to a typescript type.
Column Type | Object |
---|---|
Title | string |
Text | string |
Select | string |
Multi-select | Array (string) |
Status | string |
Number | number |
Date | Object |
Phone number | string |
string |
Querying
For each column type you’ll be presented with the available querying filter. Find all filter conditions here
While the querying functionality works, it’s not complete and there is room for user error. For instance, the filter
object should contain one child. Either the column name (signifies single filter), or and
or or
(signify compound filters). However there is no typecheck in place to stop adding multiple children
Unlike add()
, there is no transformation after the inputted object. So the querying object you’re creating is exactly what you’d normally use to query the Notion API. Learn more about them here
Example of a single filter
notion.books.query({
filter: {
genre: {
contains: "Sci-Fi",
},
},
sort: [
{
property: "name:,
direction: "ascending"
},
{
property: "Author name:,
direction: "ascending"
}
]
});
Example of compound filters, which is signified with and
and or
. You can nest these are far as you want (i.e and
filters within or
filter). Learn more here
await notion.books.query({
filter: {
or: [
{
genre: {
contains: "Sci-Fi",
},
},
{
genre: {
contains: "Biography",
},
},
],
},
});
Down below is what’s returned on a successful response. results
being a simplified extracted version of the rawResponse
(response from Notion API)
{
rawResponse: <whatever Notion API returns>,
results: [
{
bookName: "How to Change Your Mind",
genre: ["Non-fiction"],
numberPages: 460,
rating: "⭐️⭐️⭐️⭐️"
},
// ... more results
]
}
Notion API currently blocks calls from browser (per CORS)
You can get around this by creating API endpoints on stack of your choice. I’ve provided examples only for Next.js, but the high level implementation should work with any backend.
If you’re planning to only make server-side calls to your Notion database (from GetStaticProps
or GetServerSideProps
). These calls work totally fine, as these functions are executed server-side before page load. So you can ignore the proceeding steps
export const getStaticProps: GetStaticProps = async () => {
const response = await NotionClient.books.query({
filter: {
genre: {
is_not_empty: true,
},
},
});
return {
props: {
apiResponse: response
}
}
To execute calls client-side (ex. on button click) an API endpoint is needed to get around CORS. In this example we’re passing the databases DatabaseSchemaType
as the body of the API call.
import { DatabaseSchemaType } from "@haustle/notion-orm/build/db/books";
async function addPageToNotionDatabase() {
const example: DatabaseSchemaType = {
bookName: "How to Change Your Mind",
genre: ["Non-fiction"],
};
// make sure this route reflects your API path
await fetch("/api/notion/books", {
method: "POST",
body: JSON.stringify(example),
});
}
<button onClick={ async() => await addPageToNotionDatabase()}>
Example API endpoint below, where we’re taking the body of type DatabaseSchemaType
and passing it into the respected databases add()
function to add a new page to the database. Learn more about Next.js API’s and routing here.
// pages/api/notion/yourDatabaseName.ts
import type { NextApiRequest, NextApiResponse } from "next";
import {
DatabaseSchemaType,
yourDatabaseName,
} from "@haustle/notion-orm/build/db/yourDatabaseName";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { method, body } = req;
if (method === "POST") {
const bodyJSON = JSON.parse(body) as DatabaseSchemaType;
await yourDatabaseName.add(bodyJSON);
}
}