
Research
Security News
The Growing Risk of Malicious Browser Extensions
Socket researchers uncover how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
@jitl/notion-api
Advanced tools
The missing companion library for the official Notion public API.
The missing companion library for the official Notion public API.
Page
Block
..., plus helpers for tasks like
iterating paginated API results.This is not an official Notion product. The current focus of this library is on reading data from Notion.
Github | Full API documentation | NPM Package
The CMS class is a wrapper around a Notion database. A CMS instance adds the following features:
frontmatter
for each page, to
reduce page property parsing boilerplate, and provide a type-safe API for your
pages to the rest of your app.slug
property
suitable for use in a URL.import {
NotionClient, // re-exported official Notion client from peer dependencies
NotionClientDebugLogger, // enable logs with DEBUG='@jitl/notion-api:*'
CMS,
richTextAsPlainText,
} from '@jitl/notion-api';
const Recipes = new CMS({
database_id: 'a3aa29a6b2f242d1b4cf86fb578a5eea',
notion: new NotionClient({
logger: NotionClientDebugLogger,
auth: process.env.NOTION_SECRET,
}),
slug: undefined, // Use page ID
visible: true, // All pages visible
getFrontmatter: (page) => ({
/* TODO: return your custom metadata */
}),
cache: {
directory: path.join(__dirname, './cache'),
},
assets: {
directory: path.join(__dirname, './assets'),
downloadExternalAssets: true,
},
});
// Download and cache all pages in the Recipes database, and their assets.
for await (const recipe of Recipes.query()) {
console.log(
'Downloading assets for recipe: ',
richTextAsPlainText(recipe.frontmatter.title)
);
await Recipes.downloadAssets(recipe);
}
This library exports many type aliases for working with data retrieved from the
official @notionhq/client
library.
These types are derived from the official library's publicly exported types. They will be compatible with @notionhq/client, but may change in unexpected ways after a @notionhq/client update.
Abbreviated list of types: Block<BlockType>
, Page
, RichText
,
RichTextToken
, Mention<MentionType>
, Property
, PropertyFilter
, User
, etc.
There are several handy utility functions for working with those types, like
richTextAsPlainText(text)
and getPropertyValue(page, propertyPointer)
.
See the full list in the API documentation.
Dealing with pagination is annoying, but necessary to avoid resource consumption.
The iteratePaginatedAPI
helper returns an AsyncIterable<Item>
so you can
iterate over Notion API results using the for await (...) { ... }
syntax. This
should work for any paginated API using Notion's official API client.
for await (const block of iteratePaginatedAPI(notion.blocks.children.list, {
block_id: parentBlockId,
})) {
// Do something with block.
}
If you prefer a function approach and don't mind waiting for all values to load
into memory, consider asyncIterableToArray
:
const iterator = iteratePaginatedAPI(notion.blocks.children.list, {
block_id: parentBlockId,
});
const blocks = await asyncIterableToArray(iterator);
const paragraphs = blocks.filter((block) => isFullBlock(block, 'paragraph'));
The Notion API can sometimes return "partial" object data that contain only the block's ID:
// In @notionhq/client typings:
type PartialBlockObjectResponse = { object: 'block'; id: string };
export type GetBlockResponse = PartialBlockObjectResponse | BlockObjectResponse;
Checking that a GetBlockResponse
(or similar type) is a "full" block gets old
pretty fast, so this library exports type guard functions to handle common
cases, like isFullPage(page)
and isFullBlock(block)
.
isFullBlock
can optionally narrow the type of block as well:
if (isFullBlock(block, 'paragraph')) {
// It's a full paragraph block
console.log(richTextAsPlainText(block.paragraph.text));
}
Notion's API returns block data in a shape that is very difficult to deal with in a generic way while maintaining type-safety. Each block type has it's own property with the same name, and that property contains the block's data. Handling this type-safely means writing a long and annoying switch statement:
function getBlockTextContentBefore(block: Block): RichText | RichText[] {
switch (block.type) {
case 'paragraph':
return block.paragraph.rich_text;
case 'heading_1':
return block.heading_1.rich_text;
case 'heading_2':
return block.heading_2.rich_text;
// ... etc, for many more block types
default:
assertUnreachable(block); // Assert this switch is exhaustive
}
}
Enter getBlockData
. It returns a union of all possible interior data types for
a block
value. The same function can be re-written in a type-safe but
non-exhaustive way in much fewer lines:
function getBlockTextContentAfter(block: Block): RichText[] {
const blockData = getBlockData(block);
const results: RichText[] = [];
if ('rich_text' in blockData) {
results.push(blockData.rich_text);
}
if ('caption' in blockData) {
results.push(blockData.caption);
}
// Done.
return results;
}
But because this function supports narrowed block types, you can still use a
switch (block.type)
if you want to be exhaustive, and tab completion will
guide you:
function getBlockTextContentAfterExhaustive(
block: Block
): RichText | RichText[] {
switch (block.type) {
case 'paragraph': // Fall-through for blocks with only rich_text
case 'heading_1':
case 'heading_2': // ... etc
return getBlockData(block).rich_text;
case 'image':
return getBlockData(block).caption;
case 'code':
return [getBlockData(block).rich_text, getBlockData(block).caption];
// ... etc
default:
assertUnreachable(block); // Assert this switch is exhaustive
}
}
See the full list of functions in the API documentation.
API stability: This library follows SemVer, and currently has a version less that 1.0.0,
meaning it is under initial development. Do
not expect API stability between versions, so specify an exact version in
package.json
or use a lockfile (package-lock.json
, yarn.loc
etc) to
protect yourself from unexpected breaking changes.
Support: As stated above, this library not an official Notion product. I wrote it for my own use, to support my website and other projects, although I welcome contributions of any kind. There are no automated tests yet.
TypeScript: This library is developed with TypeScript 4.5.5, and is untested with other TypeScript versions.
This library is developed inside a monorepo, please see the root README.md for more information.
Run nx test notion-api
to execute the unit tests via Jest.
FAQs
The missing companion library for the official Notion public API.
The npm package @jitl/notion-api receives a total of 5 weekly downloads. As such, @jitl/notion-api popularity was classified as not popular.
We found that @jitl/notion-api demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
Research
Security News
An in-depth analysis of credential stealers, crypto drainers, cryptojackers, and clipboard hijackers abusing open source package registries to compromise Web3 development environments.
Security News
pnpm 10.12.1 introduces a global virtual store for faster installs and new options for managing dependencies with version catalogs.