Giter VIP home page Giter VIP logo

notion-api-worker's Introduction

Notion API Worker API Version

A serverless wrapper for the private Notion API. It provides fast and easy access to your Notion content. Ideal to make Notion your CMS.

We provide a hosted version of this project on https://notion-api.splitbee.io. You can also host it yourself. Cloudflare offers a generous free plan with up to 100,000 request per day.

Use with caution. This is based on the private Notion API. We can not gurantee it will stay stable.

Features

🍭 Easy to use – Receive Notion data with a single GET request

πŸ—„ Table Access – Get structured data from tables & databases

✨ Blazing Fast – Built-in SWR caching for instant results

πŸ›« CORS Friendly – Access your data where you need it

Use Cases

  • Use it as data-source for blogs and documentation. Create a table with pages and additional metadata. Query the /table endpoints everytime you want to render a list of all pages.

  • Get data of specific pages, which can be rendered with react-notion

Endpoints

Load page data

/v1/page/<PAGE_ID>

Example (Source Notion Page)

https://notion-api.splitbee.io/v1/page/2e22de6b770e4166be301490f6ffd420

Returns all block data for a given page. For example, you can render this data with react-notion.

Load data from table

/v1/table/<PAGE_ID>

Example (Source Notion Page)

https://notion-api.splitbee.io/v1/table/20720198ca7a4e1b92af0a007d3b45a4

Authentication for private pages

All public pages can be accessed without authorization. If you want to fetch private pages there are two options.

  • The recommended way is to host your own worker with the NOTION_TOKEN environment variable set. You can find more information in the Cloudflare Workers documentation.
  • Alternatively you can set the Authorization: Bearer <NOTION_TOKEN> header to authorize your requests.

Receiving the token

To obtain your token, login to Notion and open your DevTools and find your cookies. There should be a cookie called token_v2, which is used for the authorization.

Credits

notion-api-worker's People

Contributors

chromakode avatar dependabot[bot] avatar dvdsgl avatar janniks avatar pbteja1998 avatar seungminio avatar timolins avatar tobiaslins avatar transitive-bullshit avatar zephraph avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

notion-api-worker's Issues

error: Route not found

Calling whatever endpoint, like different page IDs, I always get a routing error:

{"error":"Route not found!","routes":["/v1/page/:pageId","/v1/table/:pageId","/v1/user/:pageId"]}

Cannot access preview of the the library

Hey many thanks for working on this project. I came across the project, looking for a way to make a meaningful backup of our notion knowledge repo. However the preview is not functioning at the moment.

{"error":"Route not found!","routes":["/v1/page/:pageId","/v1/table/:pageId"," /v1/user/:pageId"]}

Sorry to bother! My Bad

Bad request image URL using private notion database

Hi,

I'm getting a 400 error whenever I try to load an image that's stored as a file in a private table. I'm using my own worker with the correct NOTION_TOKEN because I can retrieve the info, but the image won't load. If I make the table publicly available then it loads just fine. Any ideas what may be the issue?

Add optional `blockMap` content in table endpoint

I think it would be cool to automatically include the blockMap of each item inside a table (optionally).
Currently I have to manually do a request for each entry to receive it. Doing it on the server with caching sounds like a good idea.

Inside toggle content returns "role: none" when using NOTION_TOKEN for private pages

Hello - I noticed while trying to read a private page on my Notion that most of my content looks okay, but when I used a toggle and placed content inside, the inside content returns JSON similar to this:

{
  "d53beb18-ae39-4422-ad5a-d23712d2d862": {
    "role": "none"
  }
}

Note that I'm passing the token as an environment variable rather than putting the AP in fetch as an Authorization Bearer. When the page becomes public the fetching of blocks work alright.

I think I've found a possible solution to this and am going to submit a PR shortly, but submitting an issue for the heads up.

Many blocks are missing from page response

After a recent update, many blocks are no longer returned for our pages. One page dropped 50-100 blocks from the end of the page.

In page bc7f910ef23b421487ca21b959c67c49, block 771f85de-f188-46e6-90d6-9b2dd814ba10 is listed in the content of the page, but the block content is not actually included.

We noticed that by deleting early blocks on the actual Notion page, missing blocks can be fetched by the API β€” in other words, blocks at the end of the page are more likely to be missing.

Throwing Error when the page is empty

Hi,

Thank you for making this API πŸ™. It has been really helpful.

Whenever I try to use the /page/<id> API on an empty page, it's throwing an error. Not sure why it's happening. Is there any way to get around this?

Request header field Authorization is not allowed by Access-Control-Allow-Headers

On Safari, when making a request as documented with the Authorization header (with bearer token) I get the error:

Request header field Authorization is not allowed by Access-Control-Allow-Headers

This seems to indicate that this package cannot actually accept the Authorization header for private notion docs.
Is there a way around this?

Does this mean that the only way to use this package for private notion docs is to host our own worker?

Feature request: honor no-cache directives

The current implementation doesn't respect no-cache directives which makes debugging more difficult.

Specifically, we should disable caching if we encounter: pragma: no-cache, cache-control: no-store, or cache-control: no-cache directives.

Private pages are returning empty objects, even with Authorization Header, and user token.

Hey, this could just be a user error, but I've tried both approaches and can't get private pages to return any BlockMap from the Splitbee Notion worker, or a personal worker that I spun up.

Personal Worker:

I created the notion worker from a local clone and uploaded it to CloudFlare:
Screen Shot 2021-02-21 at 6 27 38 PM
Screen Shot 2021-02-21 at 6 24 15 PM
Screen Shot 2021-02-21 at 6 24 07 PM

And this is what comes back when I search a private page

Screen Shot 2021-02-21 at 6 25 25 PM

This is what that said paid looks like in Notion:
Screen Shot 2021-02-21 at 6 25 46 PM

I got the token from the cookie in notion it's key was "token_v2" so I'm pretty sure I grabbed the right thing.

Please let me know if I'm doing something wrong. Thanks.

Support for authentication w. token

Hi, this package works well for my use case (grabbing data from a Notion table, nice and simple) but it would be nice to have auth support via the token.

The notion-api-agent repo has some docs on how to get your token:
https://github.com/dragonman225/notionapi-agent/blob/master/documentation/get-token/get-token.md

I've tried to add support for it but sadly tiny-request-router doesn't support headers or query params so I'd like to get your opinion first, Is it something you would like to add? And do you have thoughts about changing to a different router option?

Feature request: add search proxy

Something like POST /v1/search:

export interface SearchParams {
  query: string
  ancestorId: string
  limit?: number
}

export async function search(params: SearchParams) {
  const body = {
    type: 'BlocksInAncestor',
    source: 'quick_find_public',
    ancestorId: params.ancestorId,
    filters: {
      isDeletedOnly: false,
      excludeTemplates: true,
      isNavigableOnly: true,
      requireEditPermissions: false,
      ancestors: [],
      createdBy: [],
      editedBy: [],
      lastEditedTime: {},
      createdTime: {}
    },
    sort: 'Relevance',
    limit: params.limit || 20,
    query: params.query
  }

  return fetch('https://www.notion.so/api/v3/search', {
    method: 'POST',
    headers: {
      'content-type': 'application/json'
    },
    body: JSON.stringify(body)
  }).then((res) => res.json())
}

This is necessary to get around CORS from the client-side.

Feature request: normalize pageId before checking cache

Here's a function which extracts the pageId in a consistent format (no dashes) and ignores any additional prefix (like my-page-about-e5a735e33baa458b988993b55a54ee54):

const pageIdRe = /\b([a-f0-9]{32})$/
const pageId2Re = /\b([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/

/** Extracts the notion page ID from a notion URL suffix */
export const parsePageId = (id: string) => {
  id = id.split('?')[0]
  const match = id.match(pageIdRe)

  if (match) {
    return match[1]
  }

  const match2 = id.match(pageId2Re)
  if (match2) {
    return match2[1].replace(/-/g, '')
  }
}

And some ava unit tests that will probably be useful:

import test from 'ava'
import * as notion from './notion'

const pageIdFixturesSuccess = [
  '267c0d1f1df8457f9b5c8f7efca16d83',
  'Twitter-Automation-Tool-267c0d1f1df8457f9b5c8f7efca16d83',
  'www.notion.so/saasifysh/Twitter-Automation-Tool-267c0d1f1df8457f9b5c8f7efca16d83',
  'www.notion.so/saasifysh/Twitter-Automation-Tool-267c0d1f1df8457f9b5c8f7efca16d83?foo=bar&bar=foo',
  'https://www.notion.so/saasifysh/Standalone-Notion-Hosting-717a3608b1874cc5bafb5b9680b53395',
  'Standalone-Notion-Hosting-717a3608b1874cc5bafb5b9680b53395',
  'Standalone-Notion-Hosting-717a3608b1874cc5bafb5b9680b53395?foo',
  '-717a3608b1874cc5bafb5b9680b53395',
  '717a3608b1874cc5bafb5b9680b53395',
  '717a3608b1874cc5bafb5b9680b53395?',
  'e5a735e3-3baa-458b-9889-93b55a54ee54',
  'fde5ac74-eea3-4527-8f00-4482710e1af3',
  'about-e5a735e3-3baa-458b-9889-93b55a54ee54',
  '.com/about-e5a735e3-3baa-458b-9889-93b55a54ee54',
  'About-d9ae0c6e7cad49a78e21d240cf2e3d04'
]

const pageIdFixturesFailure = [
  '717A3608b1874CC5bafb5b9680b53395',
  '717A36',
  '',
  'notion.so/saasifysh/Twitter-Automation-Tool-267c0d1f1df8457f9b5c8f7efca16d83abc',
  'a267c0d1f1df8457f9b5c8f7efca16d83',
  '267c0d1f1df8457f9b5c8f7efca16d83a',
  '267c0d1f1%f8457f9b5c8f7efca16d83',
  'Twitter-Automation-Tool',
  'fde5ac74-eea3-4527-8f00-4482710e1af'
]

test('notion.parsePageId success', (t) => {
  for (const id of pageIdFixturesSuccess) {
    const parsedId = notion.parsePageId(id)
    t.truthy(parsedId)
    t.snapshot(parsedId)
  }
})

test('notion.parsePageId failure', (t) => {
  for (const id of pageIdFixturesFailure) {
    const parsedId = notion.parsePageId(id)
    t.falsy(parsedId)
  }
})

Feature request: integration tests

I recommend using HTTP response fixtures from real notion API responses via nock and making sure that requests to this server always return the same result given the same inputs either via jest or ava snapshots.

Writing notion blocks?

Curious if you folks have experimented with writing data back to Notion? What does the format of a block look like? Markdown, a JSON schema? Something else?

Proposal: Add default cache-control

cache.put(request, response) uses the response's cache-control header to determine the object's TTL (and other standard headers).

Currently, we're passing responses with no cache-control which isn't really well-specified by Cloudflare as to what this actually means.

Experimentally, it appears that CF caches these objects for ~10 seconds, but we should really override this with a default value.

Note that for Notion2Site, I'm passing pragma: no-cache to disable caching within the worker because Vercel's SWR edge caching is all that's needed. Any additional caching of the page data just introduces additional lag before updates are reflected to their edge cache.

[security flaw] private pages cached

I have a use case where multiple users will be making use of a tool and so self-hosting and changing the NOTION_TOKEN as recommended wouldn't be a viable solution.

I've been adding the Authorization: Bearer <NOTION_TOKEN> header to my requests to private pages, but have noticed that because those pages are cached for a few seconds I if I then request the same URL without the authorisation header I can load private page data (tested from a separate device, so it's not just browser caching).

Either private pages shouldn't be cached, or their authorisations should be cached with them to prevent this.

Nested block aren't rendering

Hello,

Let's see a screenshot of my notion page (available here)

image

And now, let's see a screenshot of my notion integration on my portfolio

image

As you can see, the content that are nested into the toggle heading aren't displaying. When I turn the toggle heading in classical heading, the nested content are successfully displaying.

Here is my code

NotionEmbed.jsx

import React, {useEffect, useState} from 'react'
import { useTranslation } from 'react-i18next'
import { NotionRenderer } from 'react-notion'
import "react-notion/src/styles.css"

export default function NotionEmbed(props) {
  const { t, i18n } = useTranslation();
  const { notionPageId } = props;
  let [notionContent, setNotionContent] = useState({});

    useEffect(() => {
        async function getStaticProps() {
            const data = await fetch(
                `https://notion-api.splitbee.io/v1/page/${notionPageId}`
            ).then(res => res.json());
            setNotionContent(data);
        }
        getStaticProps();
    }, []);

    useEffect(() => {
        console.log(notionContent)
    }, [notionContent]);

  return (
    <>
        <div className="notion-embed">
            <NotionRenderer blockMap={notionContent} fullPage={true} />
        </div>
    </>
  )
}

Courses.jsx (the component where the notion embed is used)

import './Courses.scss'
import React from 'react'
import { useTranslation } from 'react-i18next'
import NotionEmbed from "../NotionEmbed/NotionEmbed";

export default function Courses() {
  const { t, i18n } = useTranslation();
  return (
    <>
      <section id="section-courses">
        <h2 className="courses-title">
          {t('courses.MainTitle')}{' '}
            <span className="color-tertiary">{t('courses.TitleSpan')}</span>
        </h2>
        <div className="courses-container">
            <NotionEmbed notionPageId="1a9d25ee-18f8-4b92-98a3-d1a1e8149863" />
        </div>
      </section>
    </>
  )
}

And finally, here is my blockMap content :

pastebin

Could smbdy help me with this issue ? Thank you guys πŸ’―

Getting AccessDenied on uploaded images

I am getting this error when I am using images from my notion doc
image

This is my notionId: 96defe8701444f0782cc9b428929c79a

I am rendering the page content using react-notion-x, those are working fine but the image URLs I am getting from the payload are not working

Would appreciate any help on this one 😊

Cannot read properties of undefined (reading 'block') Exception

I'm trying to deploy to cloudflare worker and after cloning and building the repository with the updated attributes, I get the following exception raised whilst trying to access the page;

{
  "outcome": "exception",
  "scriptName": "notion-cloudflare-worker",
  "exceptions": [
    {
      "name": "TypeError",
      "message": "Cannot read properties of undefined (reading 'block')",
      "timestamp": 1678203099458
    }
  ],
  "logs": [],
  "eventTimestamp": 1678203099274,
  "event": {
    "request": {
      "url": "https://notion-cloudflare-worker.XYZ.workers.dev/v1/page/REDACTED",
      "method": "GET",
      "headers": {
        "accept": "application/json, text/plain, */*",
        "accept-encoding": "gzip",
        "authorization": "REDACTED",
        "cf-connecting-ip": "86.8.247.150",
        "cf-ipcountry": "GB",
        "cf-ray": "7a43e2fa683c3867",
        "cf-visitor": "{\"scheme\":\"https\"}",
        "connection": "Keep-Alive",
        "host": "notion-cloudflare-worker.XYZ.workers.dev",
        "user-agent": "axios/0.21.1",
        "x-forwarded-proto": "https",
      },
      "cf": {
        "clientTcpRtt": 16,
        "longitude": "-2.55310",
        "latitude": "51.47710",
        "tlsCipher": "AEAD-AES128-GCM-SHA256",
        "continent": "EU",
        "asn": 5089,
        "country": "GB",
        "tlsClientAuth": {
          "certIssuerDNLegacy": "",
          "certIssuerSKI": "",
          "certSubjectDNRFC2253": "",
          "certSubjectDNLegacy": "",
          "certFingerprintSHA256": "",
          "certNotBefore": "",
          "certSKI": "",
          "certSerial": "",
          "certIssuerDN": "",
          "certVerified": "NONE",
          "certNotAfter": "",
          "certSubjectDN": "",
          "certPresented": "0",
          "certRevoked": "0",
          "certIssuerSerial": "",
          "certIssuerDNRFC2253": "",
          "certFingerprintSHA1": ""
        },
        "tlsVersion": "TLSv1.3",
        "colo": "LHR",
        "timezone": "Europe/London",
        "city": "Bristol",
        "edgeRequestKeepAliveStatus": 1,
        "requestPriority": "",
        "httpProtocol": "HTTP/1.1",
        "region": "England",
        "regionCode": "ENG",
        "asOrganization": "Virgin Media",
        "postalCode": "REDACTED"
      }
    },
    "response": {
      "status": 500
    }
  },
  "id": 12
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.