Notion
You can enable the Notion wrapper right from the Supabase dashboard.
Open wrapper in dashboardNotion provides a versatile, ready-to-use solution for managing your data.
The Notion Wrapper is a WebAssembly(Wasm) foreign data wrapper which allows you to read data from your Notion workspace for use within your Postgres database.
Available Versions
| Version | Wasm Package URL | Checksum | Required Wrappers Version |
|---|---|---|---|
| 0.2.0 | https://github.com/supabase/wrappers/releases/download/wasm_notion_fdw_v0.2.0/notion_fdw.wasm | 719910b65a049f1d9b82dc4f5f1466457582bec855e1e487d5c3cc1e6f986dc6 | >=0.5.0 |
| 0.1.1 | https://github.com/supabase/wrappers/releases/download/wasm_notion_fdw_v0.1.1/notion_fdw.wasm | 6dea3014f462aafd0c051c37d163fe326e7650c26a7eb5d8017a30634b5a46de | >=0.4.0 |
| 0.1.0 | https://github.com/supabase/wrappers/releases/download/wasm_notion_fdw_v0.1.0/notion_fdw.wasm | e017263d1fc3427cc1df8071d1182cdc9e2f00363344dddb8c195c5d398a2099 | >=0.4.0 |
Preparation
Before you can query Notion, you need to enable the Wrappers extension and store your credentials in Postgres.
Enable Wrappers
Make sure the wrappers extension is installed on your database:
1create extension if not exists wrappers with schema extensions;Enable the Notion Wrapper
Enable the Wasm foreign data wrapper:
1create foreign data wrapper wasm_wrapper2 handler wasm_fdw_handler3 validator wasm_fdw_validator;Store your credentials (optional)
By default, Postgres stores FDW credentials inside pg_catalog.pg_foreign_server in plain text. Anyone with access to this table will be able to view these credentials. Wrappers is designed to work with Vault, which provides an additional level of security for storing credentials. We recommend using Vault to store your credentials.
1-- Save your Notion API key in Vault and retrieve the created `key_id`2select vault.create_secret(3 '<Notion API key>', -- Notion API key, should look like ntn_589513........4 'notion',5 'Notion API key for Wrappers'6);⚠️ ** Getting a Notion API key**
- Visit Notion > Profile > Integrations
- Click
New integration- Add an integration name, select your workspace, then select Internal as the Type
- This will give you an
Internal Integration Secretthat will look likentn_589513........- Use this as your Notion API key
Connecting to Notion
We need to provide Postgres with the credentials to access Notion and any additional options. We can do this using the create server command:
1create server notion_server2 foreign data wrapper wasm_wrapper3 options (4 fdw_package_url 'https://github.com/supabase/wrappers/releases/download/wasm_notion_fdw_v0.1.1/notion_fdw.wasm',5 fdw_package_name 'supabase:notion-fdw',6 fdw_package_version '0.1.1',7 fdw_package_checksum '6dea3014f462aafd0c051c37d163fe326e7650c26a7eb5d8017a30634b5a46de',8 api_url 'https://api.notion.com/v1', -- optional9 api_key_id '<vault key_ID>' -- the Vault key id from the previous step, not the Notion API key itself10 );Note the fdw_package_* options are required, which specify the Wasm package metadata. You can get the available package version list from above.
Create a schema
We recommend creating a schema to hold all the foreign tables:
1create schema if not exists notion;Options
The full list of foreign table options are below:
object- Object name in Notion, required.
Supported objects are listed below:
| Object name |
|---|
| block |
| page |
| database |
| user |
Entities
We can use SQL import foreign schema to import foreign table definitions from Notion.
For example, using below SQL can automatically create foreign tables in the notion schema.
1-- create all the foreign tables2import foreign schema notion from server notion_server into notion;34-- or, create selected tables only5import foreign schema notion6 limit to ("blocks", "pages")7 from server notion_server into notion;89-- or, create all foreign tables except selected tables10import foreign schema notion11 except ("blocks")12 from server notion_server into notion;Block
This is an object representing Notion Block content.
Ref: Notion API docs
Operations
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| Block | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage
1create foreign table notion.blocks (2 id text,3 page_id text,4 type text,5 created_time timestamp,6 last_edited_time timestamp,7 archived boolean,8 attrs jsonb9)10 server notion_server11 options (12 object 'block'13 );Notes
- The
attrscolumn contains all user attributes in JSON format - The
page_idfield is added by the FDW for development convenience - All blocks, including nested children blocks, belong to one page will have the same
page_id - Query pushdown supported for both
idandpage_idcolumns - Use
page_idfilter to fetch all blocks of a specific page recursively - Querying all blocks without filters may take a long time due to recursive data requests
Page
This is an object representing Notion Pages.
Ref: Notion API docs
Operations
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| Page | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage
1create foreign table notion.pages (2 id text,3 url text,4 created_time timestamp,5 last_edited_time timestamp,6 archived boolean,7 attrs jsonb8)9 server notion_server10 options (11 object 'page'12 );Notes
- The
attrscolumn contains all page attributes in JSON format - Query pushdown supported for
idcolumn
Database
This is an object representing Notion Databases.
Ref: Notion API docs
Operations
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| Database | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage
1create foreign table notion.databases (2 id text,3 url text,4 created_time timestamp,5 last_edited_time timestamp,6 archived boolean,7 attrs jsonb8)9 server notion_server10 options (11 object 'database'12 );Notes
- The
attrscolumn contains all database attributes in JSON format - Query pushdown supported for
idcolumn
User
This is an object representing Notion Users.
Ref: Notion API docs
Operations
| Object | Select | Insert | Update | Delete | Truncate |
|---|---|---|---|---|---|
| User | ✅ | ❌ | ❌ | ❌ | ❌ |
Usage
1create foreign table notion.users (2 id text,3 name text,4 type text,5 avatar_url text,6 attrs jsonb7)8 server notion_server9 options (10 object 'user'11 );Notes
- The
attrscolumn contains all user attributes in JSON format - Query pushdown supported for
idcolumn - User email can be extracted using:
attrs->'person'->>'email'
Query Pushdown Support
This FDW supports where clause pushdown with id as the filter. For example,
1select * from notion.pages2where id = '5a67c86f-d0da-4d0a-9dd7-f4cf164e6247';will be translated to a Notion API call: https://api.notion.com/v1/pages/5a67c86f-d0da-4d0a-9dd7-f4cf164e6247.
In addition to id column pushdown, page_id column pushdown is also supported for Block object. For example,
1select * from notion.blocks2where page_id = '5a67c86f-d0da-4d0a-9dd7-f4cf164e6247';will recursively fetch all children blocks of the Page with id '5a67c86f-d0da-4d0a-9dd7-f4cf164e6247'. This can dramatically reduce number of API calls and improve query performance.
Below query will request ALL the blocks of ALL pages recursively, it may take very long time to run if there are many pages in Notion. So it is recommended to always query Block object with an id or page_id filter like above.
1select * from notion.blocks;Supported Data Types
| Postgres Data Type | Notion Data Type |
|---|---|
| boolean | Boolean |
| text | String |
| timestamp | Time |
| timestamptz | Time |
| jsonb | Json |
The Notion API uses JSON formatted data, please refer to Notion API docs for more details.
Limitations
This section describes important limitations and considerations when using this FDW:
- Large result sets may experience slower performance due to full data transfer requirement
- Query pushdown support limited to 'id' and 'page_id' columns only
- Recursive block fetching can be extremely slow for large page hierarchies
- Materialized views using these foreign tables may fail during logical backups
Examples
Basic Example
This example will create a "foreign table" inside your Postgres database and query its data.
1create foreign table notion.pages (2 id text,3 url text,4 created_time timestamp,5 last_edited_time timestamp,6 archived boolean,7 attrs jsonb8)9 server notion_server10 options (11 object 'page'12 );1314-- query all pages15select * from notion.pages;1617-- query one page18select * from notion.pages19where id = '5a67c86f-d0da-4d0a-9dd7-f4cf164e6247';attrs is a special column which stores all the object attributes in JSON format, you can extract any attributes needed from it. See more examples below.
Query JSON Attributes
1create foreign table notion.users (2 id text,3 name text,4 type text,5 avatar_url text,6 attrs jsonb7)8 server notion_server9 options (10 object 'user'11 );1213-- extract user's email address14select id, attrs->'person'->>'email' as email15from notion.users16where id = 'fd0ed76c-44bd-413a-9448-18ff4b1d6a5e';Query Blocks
1-- query ALL blocks of ALL pages recursively, may take long time!2select * from notion.blocks;34-- query a single block by block id5select * from notion.blocks6where id = 'fc248547-83ef-4069-b7c9-18897edb7150';78-- query all block of a page by page id9select * from notion.blocks10where page_id = '5a67c86f-d0da-4d0a-9dd7-f4cf164e6247';