Awesome
notion-helper
A heaping spoonful of syntactic sugar for the Notion API.
This library is mainly built to help you create pages and blocks without writing so many nested objects and arrays by hand.
All functions and methods have JSDoc markup to support IntelliSense.
Check out the library's website for additional examples.
Full documentation on all functions can methods can be found here.
Installation
This package is ESM-only.
Install via npm:
npm install notion-helper
TypeScript Support
The notion-helper package has TypeScript support via declarations generated from the JSDocs comments in the source files. If you're using TypeScript, you'll get full type chcking and auto-completion in your editor when using notion-helper.
Usage
Import the package:
import NotionHelper from "notion-helper";
From here, you can destructure the functions to use them directly, or just call NotionHelper.
const { makeParagraphBlocks } = NotionHelper;
const quotes = [
"Dear frozen yogurt, you are the celery of desserts. Be ice cream, or be nothing.",
"Give me all the bacon and eggs you have.",
"There is no quiet anymore. There is only Doc McStuffins.",
];
const paragraphBlocks = makeParagraphBlocks(quotes);
// or...
const paragraphBlocks = NotionHelper.makeParagraphBlocks(quotes);
Notion Helper currently contains five direct functions you can use:
quickPages()
- lets you easily create multiple valid page objects with property values and child blocks using very simple JSON objects and a simple schema object.createNotion()
- a powerful factory function that can build full page objects, property objects, or child block arrays through method-chaining.makeParagraphBlocks()
- takes an array of strings and returns an array of Paragraph blocks without any special formatting or links. Provides a very quick way to prep a lot of text for sending to Notion.buildRichTextObj()
- takes a string, options array, and URL and creates a Rich Text Object array (use flatMap() if you're inserting its output into another array). Splits strings over the character limit for you as well. Currently only works for text objects; mentions and equations aren't supported yet..setIcon()
- takes a string, which should be a single emoji (π΅) or an image URL and returns the correct object (emoji or external) value for anicon
property.
Using quickPages()
Often you'll have an array of relatively simple data that you want to turn into multiple Notion pages:
const tasks = [
{
icon: "π¨",
task: "Build Standing Desk",
due: "2024-09-10",
status: "Not started",
},
{
task: "Mount Overhead Lights",
due: "2024-09-11",
status: "Not started",
children: [
"Mount clamp to joist and tighten",
"Attach arm to clamp",
"Mount video light to arm",
"Run power cable through ceiling panels"
],
},
{ task: "Set Up Camera", due: "2024-09-11", status: "Not started" },
];
Say you want to create a page in a Tasks database for each of these, setting the relevant properties and creating a list of To-Do blocks in the page body for any that have a children
array.
[
{
parent: { type: 'database_id', database_id: 'abcdefghijklmnopqrstuvwxyz' },
icon: { type: 'emoji', emoji: 'π¨' },
properties: {
Name: {
title: [
{
type: 'text',
text: { content: 'Build Standing Desk' },
annotations: {}
}
]
},
'Due Date': { date: { start: '2024-09-10', end: null } },
Status: { status: { name: 'Not started' } }
}
},
{
parent: { type: 'database_id', database_id: 'abcdefghijklmnopqrstuvwxyz' },
properties: {
Name: {
title: [
{
type: 'text',
text: { content: 'Mount Overhead Lights' },
annotations: {}
}
]
},
'Due Date': { date: { start: '2024-09-11', end: null } },
Status: { status: { name: 'Not started' } }
},
children: [
{
type: 'to_do',
to_do: {
rich_text: [
{
type: 'text',
text: { content: 'Mount clamp to joist and tighten' },
annotations: {}
}
],
checked: false,
color: 'default',
children: []
}
},
{
type: 'to_do',
to_do: {
rich_text: [
{
type: 'text',
text: { content: 'Attach arm to clamp' },
annotations: {}
}
],
checked: false,
color: 'default',
children: []
}
},
{
type: 'to_do',
to_do: {
rich_text: [
{
type: 'text',
text: { content: 'Mount video light to arm' },
annotations: {}
}
],
checked: false,
color: 'default',
children: []
}
},
{
type: 'to_do',
to_do: {
rich_text: [
{
type: 'text',
text: { content: 'Run power cable through ceiling panels' },
annotations: {}
}
],
checked: false,
color: 'default',
children: []
}
}
]
},
{
parent: { type: 'database_id', database_id: 'abcdefghijklmnopqrstuvwxyz' },
properties: {
Name: {
title: [
{
type: 'text',
text: { content: 'Set Up Camera' },
annotations: {}
}
]
},
'Due Date': { date: { start: '2024-09-11', end: null } },
Status: { status: { name: 'Not started' } }
}
}
]
</details>
Using quickPages()
, you can create an array of valid page objects like the one in the toggle above.
With it, you can pass an options object with:
parent
: A parent page/databaseparent_type
: The parent's type ("page_id" or "database_id")pages
: Your array of objectsschema
: A schema describing how your object's properties map to Notion property names and typeschildrenFn
: An optional callback that will be excuted on all array elements in any object'schildren
property, creating achildren
array within the Notion page object
[!TIP] You can also pass a single object, but you'll still get an array back.
The schema object should contain a property for each property in the Notion database you'd like to edit. For each, the key should map to a key present in at least one of your objects, and its value should be an array containing the matching Notion database property's name and type.
Valid property types include all those listed in the page_props
section below.
[!NOTE] If the
parent
has the typepage_id
(i.e. it's a page, not a database), the only valid value will be["title", "title"]
, which represent's the page's title.
Here's an example of simple usage, operating on the tasks
array shown above:
const propertySchema = {
task: ["Name", "title"],
due: ["Due Date", "date"],
status: ["Status", "status"],
}
const pages = quickPages({
parent: database_id,
parent_type: "database_id",
pages: tasks,
schema: propertySchema,
})
/* Create a page for each returned page object */
const responses = await Promise.all(
pages.map((page) => notion.pages.create(page))
)
Optionally, your schema can also include custom properties that represent the icon, cover, and children. If you want to specify these, use the convention below to tell the function which properties correspond to the icon
, cover
, and children
properties.
By default, the function will look for icon
, cover
, and children
in your pages object, so you don't need to specify those keys in your schema if they're named that way.
const schema = [
favicon: ["Icon", "icon"],
bg_image: ["Cover", "cover"],
body: ["Children", "children"]
]
By default, if you have a string or an array of strings in any object's children
property, those strings will be turned into Paragraph blocks.
However, you can use the childrenFn
parameter to pass a callback function. All array elements in each object's children
property (if present) will be run through this callback. childrenFn
should take in an array as its argument and return an array. The returned array will be used directly as the children
array in the final page object.
In the example below, childrenFn
is used to create a To-Do block for each children item:
const pages = quickPages({
parent: database_id,
parent_type: "database_id",
pages: tasks,
schema: propertySchema,
childrenFn: (value) => value.map((line) => NotionHelper.block.to_do.createBlock({rtArray: NotionHelper.buildRichTextObj(line)}))
})
Note how other Notion Helper functions are used in this example callback to easily construct the To-Do blocks.
If an object's children
property already contains an array of prepared Block objects, simply make it the return value of childrenFn
. Otherwise, quickPages()
will treat it as invalid children content and strip it out. Without a childrenFn
, quickPages()
will only automatically process a string or array of strings in a children
property.
const pages = quickPages({
parent: database_id,
parent_type: "database_id",
pages: tasks,
schema: propertySchema,
childrenFn: (value) => value
})
Using createNotion()
Read the full createNotion() reference here.
The createNotion
function lets you easily build high-level Notion API data structures using simple method-chaining syntax. It can create:
- Full page objects that can be passed direct as the argument when creating a page
- Property objects that can be added to preconstucted page objects
- Arrays of blocks that can be passed to the
children
property of a page object or Append Block Children request.
This function can also help you deal with large amounts of data that would exceed the Notion API's request size limits.
It returns an object with one or two properties:
content
: The constructed page object, property object, or children arrayadditionalBlocks
: If you constructed achildren
array with more than 100 blocks, this property will contain an array of arrays, each with up to 100 blocks (not including the first 100, which can be send in the initial request as part ofcontent
.)
This function has methods for all supported Notion property types, block types, and page meta types, and provides shorthand names for all of them.
When creating blocks, it will perform length-checks on strings passed to the block methods. paragraph()
will split strings over the 2,000-character limit into multiple paragraph blocks. All other rich-text blocks (headings, lists, quotes, callouts, etc) will split strings over 2,000 characters into multiple rich_text objects, but will keep them in the same block.
It also provides generic methods:
property()
addBlock()
Additionally, it supports nesting as much as is allowed by the API. You can set a block as a parent block by using the startParent()
method, defining the block's details just as you would with addBlock()
.
All block methods you chain to startParent()
will be children of that block until you add endParent()
to the chain.
Example usage:
// Goal: Create a page in an Albums database with Name, Artist, and Release Date properties.
// Page content should include Tracklist and Album Art sections.
const album = {
name: "Mandatory Fun",
artist: `"Weird Al" Yankovic`,
release_date: "07/15/2014",
cover: "https://m.media-amazon.com/images/I/81cPt0wKVIL._UF1000,1000_QL80_.jpg",
tracks: [
"Handy (Parody of Fancy by Iggy Azalea)",
"Lame Claim to Fame (Style Parody of Southern Culture on the Skids)",
"Foil (Parody of Royals by Lorde)",
"Sports Song (Style Parody of College Football Fight Songs)",
"Word Crimes (Parody of Blurred Lines by Robin Thicke)",
"My Own Eyes (Style Parody of Foo Fighters)",
"NOW Thatβs What I Call Polka!",
"Mission Statement (Style Parody of Crosby, Stills & Nash)",
"Inactive (Parody of Radioactive by Imagine Dragons)",
"First World Problems (Style Parody of Pixies)",
"Tacky (Parody of Happy by Pharrell Williams)",
"Jackson Park Express (Style Parody of Cat Stevens)"
]
}
// The target database
const database_id = "41eb98ef0a1ec4a6c91tq2thrb2930a";
// Create a builder instance and chain methods to add the page details
const builder = createNotion()
.parentDb(database_id)
.title("Name", album.name)
.richText("Artist", album.artist)
.date("Released", album.release_date)
.heading1("Tracklist")
.loop("numbered_list_item", album.tracks) // The loop() method can create blocks from an array
.heading1("Album Art")
.image(album.cover)
.build() // Call build() at the end of the chain
// We called parentDb(), so builder.content is a page object we can use
// to create a new page in our target database
const response = await notion.pages.create(builder.content)
The result:
<details> <summary>You can also create more complex page structures with tables and child-block nesting:</summary>// A more complex album object.
// Goal: Turn the track list into a Notion Table within the album's page
const album = {
name: "Mandatory Fun",
artist: `"Weird Al" Yankovic`,
release_date: "07/15/2014",
cover: "https://m.media-amazon.com/images/I/81cPt0wKVIL._UF1000,1000_QL80_.jpg",
tracks: [
{
"No.": 1,
Title: "Handy",
"Writer(s)":
"Amethyst Kelly\nCharlotte Aitchison\nGeorge Astasio\nJason Pebworth\nJonathan Shave\nKurtis McKenzie\nJon Turner\nAl Yankovic",
"Parody of": 'Fancy" by Iggy Azalea featuring Charli XCX',
Length: "2:56",
},
{
"No.": 2,
Title: "Lame Claim to Fame",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of Southern Culture on the Skids[79]",
Length: "3:45",
},
{
"No.": 3,
Title: "Foil",
"Writer(s)": "Joel Little\nElla Yelich-O'Connor\nYankovic",
"Parody of": 'Royals" by Lorde',
Length: "2:22",
},
{
"No.": 4,
Title: "Sports Song",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of college football fight songs[25]",
Length: "2:14",
},
{
"No.": 5,
Title: "Word Crimes",
"Writer(s)":
"Robin Thicke\nPharrell Williams\nClifford Harris Jr.\nMarvin Gaye1\nYankovic",
"Parody of":
'Blurred Lines" by Robin Thicke featuring T.I. and Pharrell Williams',
Length: "3:43",
},
{
"No.": 6,
Title: "My Own Eyes",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of Foo Fighters[79]",
Length: "3:40",
},
{
"No.": 7,
Title: "Now That's What I Call Polka!",
"Writer(s)": "showVarious writers:",
"Parody of": "showA polka medley including:",
Length: "4:05",
},
{
"No.": 8,
Title: "Mission Statement",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of Crosby, Stills & Nash[79]",
Length: "4:28",
},
{
"No.": 9,
Title: "Inactive",
"Writer(s)":
"Alexander Grant\nDaniel Reynolds\nDaniel Sermon\nBenjamin McKee\nJoshua Mosser\nYankovic",
"Parody of": 'Radioactive" by Imagine Dragons',
Length: "2:56",
},
{
"No.": 10,
Title: "First World Problems",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of Pixies[79]",
Length: "3:13",
},
{
"No.": 11,
Title: "Tacky",
"Writer(s)": "Williams\nYankovic",
"Parody of": 'Happy" by Pharrell Williams',
Length: "2:53",
},
{
"No.": 12,
Title: "Jackson Park Express",
"Writer(s)": "Yankovic",
"Parody of": "Style parody of Cat Stevens[79]",
Length: "9:05",
},
],
};
// The target database
const database_id = "41eb98ef0a1ec4a6c91tq2thrb2930a";
const builder = createNotion()
.parentDb(database_id)
.title("Name", album.name)
.richText("Artist", album.artist)
.date("Released", album.release_date)
.heading1("Tracklist")
.startParent("table", { // startParent() creates a block and chains further blocks as its children
has_column_header: true,
rows: [["No", "Title", "Writer(s)", "Parody of", "Length"]],
})
.loop((builder, track) => { // loop() can accept a callback for custom handling instead of a block type
builder.tableRow([
track["No."], track.Title, track["Writer(s)"], track["Parody of"], track.Length
])
}, album.tracks)
.endParent() // endParent() to break out of the table block's children array
.heading1("Album Art")
.image(album.cover)
.build();
const response = notion.pages.create(builder.content);
</details>
By default, createNotion()
will try to smoothly handle null/undefined values passed to its methods, returning this
and effectively ignoring the method call. This can be helpful when you're looping over an array of objects with inconsistent keys, or handling user input where even specific properties may or may not be defined by the user.
However, you can call createNotion({ strict: true })
if you'd like to enable strict mode. When enabled, null/undefined block types, property names, property types, and property/block values passed will cause the function to throw an error.
Notion-Helper also provides objects with methods for quickly creating pages and blocks:
block
The block
object lets you create most supported block types while writing less code. It supports these block types:
- Bookmark
- Bulleted List Item
- Callout
- Code
- Divider
- Embed
- File
- Heading 1
- Heading 2
- Heading 3
- Image
- Numbered List Item
- Paragraph
- Quote
- Table
- Table Row
- Table of Contents
- To-Do
- Toggle
- Video
Some block types will return a null
if they are provided with invalid input. You should filter null
entries out of your children
array before adding it to an API call.
Each block type has a createBlock() method you can call, which takes an object containing properties specific to that block type. Most take rich_text
, which is an array of Rich Text objects you can easily create with builtRichTextObj()
. You can also pass a single string or an array of strings in rich_text
, or just pass a string or array of strings as the sole argument. Notion-Helper will coerce strings to rich text objects where needed.
Examples:
const headerText = "How to Play Guitar with Your Teeth";
const heading1 = NotionHelper.block.heading_1.createBlock({
rich_text: buildRichTextObj(headerText),
});
page_meta
The page_meta
object lets you quickly set the parent, icon, and cover for a page. Pages can be standalone or within a database.
The parent
property's createMeta()
method takes an object containing the parent page ID and type, while the icon
and cover
properties require only a string representing an externally-hosted image file (or single emoji π€ in the case of icon
).
const page = {
parent: NotionHelper.page_meta.parent.createMeta({
id: parentID,
type: "database",
}),
icon: NotionHelper.page_meta.icon.createMeta("π"),
cover: NotionHelper.page_meta.cover.createMeta(
"https://i.imgur.com/5vSShIw.jpeg"
),
};
page_props
The page_props
object lets you quickly set the property values of a Notion page. Pages can be standalone or in a database, though standalone pages can only have a title
property.
Each property represents a database property type. All writeable properties are supported:
- Title
- Rich Text
- Checkbox
- Date
- Files
- Multi-Select
- Number
- People
- Phone Number
- Relation
- Select
- Status
- URL
Each property's setProp()
takes an argument as specified by the Page Properties specification of the Notion API. (E.g. Checkbox props take a boolean
, Rich Text props take an array of Rich Text objects, Date props take an ISO-8601 date-time string, etc.)
const page = {
/* parent, icon, cover */
properties: {
Name: NotionHelper.page_props.title.setProp(buildRichTextObj("Flylighter - Notion Web Clipper")),
Capture Date: NotionHelper.page_props.date.setProp(new Date().toISOString())
URL: NotionHelper.page_props.url.setProp("https://flylighter.com/")
}
}
Learn More
If you'd like to learn the Notion API from scratch, start with my free Notion API crash course.
Questions? Ask me on Twitter!