【手順を丁寧に解説】モバイルアプリでNotion API(Public integration)を実装する

Featured image of the post

はじめに

はじめてNotion API(Public integration)を使うとき、情報が少なく実装に苦戦した😢

この記事では苦戦した経験をもとに、モバイルアプリにNotion API(Public integration)を実装する手順を丁寧に解説する✨

💡
今回はReact Native(Expo)で解説するが、他の言語でも流れは同じ。

対象読者
  • モバイルアプリにNotion API(Public Integration)を実装したい方

この記事で作るもの

アプリ利用者がNotionと連携して、APIが使える状態にする

(最後におまけでAPIの実行も解説する)

Image in a image block

💡
今回紹介する方法はあくまで実装方法の一例です。

他にいい方法があればX(@rakuraku_eng)にメッセージいただけると嬉しいです!

解決したい課題

初心者が一から調べてNotion API(Public integration)を実装するのは大変💦

この記事では、初心者でも手順どおりに真似すればNotion API(Public integration)を実装できるように解説する😊

💡
具体的に「実装が大変な箇所」は後述する。

Notion API(Public integration)って何?

Notion APIには2種類のインテグレーションがある

前提としてNotion APIを使うには、インテグレーション(APIキーのようなもの)が必要。

インテグレーションは2種類あり、Public Integrationはそのうちの1種類。

  • Internal Integration:自分用のアプリ開発で使うインテグレーション。
  • Public integration:多数のユーザーに配布するアプリ開発で使うインテグレーション。

【補足】Internal Integrationの詳細

✅一言でいうと非公開用のインテグレーション。

アプリ利用者全員Notionの開発者ページに行って、自分用のインテグレーションを発行する必要がある。

  • メリット :実装が簡単
  • デメリット:ユーザーが面倒(インテグレーションを発行するのが手間)
  • 主な用途 :自分でAPIを使用するとき

【補足】Public integrationの詳細

✅一言でいうと公開用のインテグレーション。

開発者だけNotionの開発者ページに行って、インテグレーションを発行すればいい。(皆で1つのインテグレーションを使う)

  • メリット :ユーザーが楽(インテグレーションを発行する手間がない)
  • デメリット:実装が難しい
  • 主な用途 :多数のユーザーにAPIを使ってもらうするとき
  • イメージ :利用者はアクセス許可するだけでNotion APIを利用できる✨

    ※アクセス許可の画面

    Image in a image block

Public integrationが難しく感じる理由

1つ1つの手順はそれほどややこしくないが、使用する技術が多いせいで、知らないものがあると難しく感じてしまう。

💡
すべての技術を理解するのは初心者にはハードルが高い😢

今回はこれらを知らない人でも実装できるように向けに丁寧に解説する✨

【補足】主な使用技術
  • OAuth2.0

    →アクセストークンを取得するためにOAuth2.0を使う。

  • ディープリンク

    →アクセストークンを受け取る過程で必要。

    (Webページを開く処理が出てくる。そのときモバイルアプリに戻ってくるためにディープリンクが必要。

  • GitHub Pages(Webページを作成)

    →アクセストークンを受け取る過程で必要。

    (Notion側の仕様でモバイルアプリでは一時トークンを受け取れない。一時トークンを受け取るためにWebページが必要。)

  • AWS Lambda(APIエンドポイントを作成)

    →アクセストークンを受け取る過程で必要。

    (セキュリティ的にモバイルアプリから直接アクセストークンを発行するのはNG。間にAWS Lambdaが必要。)

Public Integrationを使うためのポイントまとめ

✅Public Integrationを使うには、OAuthの認可が必要。

アクセストークンを取得する必要がある。

→OAuthの認可ができれば、あとは難しいところはほとんどない。

全体の流れ

✅とにかくアクセストークンを取得すればOK!

アクセストークンを取得するには7ステップの作業が必要。

💡
7ステップを2つの画像に分けて解説する!

①②③一時トークンをもらう
Image in a image block

やりたいこと

✅アクセストークンを取得するため、まずは一時トークンをもらいたい!

ポイント
  • Notion側の仕様制限でNotionから直接モバイルアプリに一時トークンを送れない💦

    (Webページにしか一時トークンを送れない)

  • つまり必ずどこかのWebページを経由しないといけない💦

結論
  • 簡単なWebページを作らないといけない。

    (GitHub Pagesなどの無料サービスでOK)

④⑤⑥⑦アクセストークンをもらう
Image in a image block

やりたいこと

アクセストークンをもらいたい!

ポイント
  • 一時トークン(③で取得したもの)をNotionのサーバーに送れば、アクセストークンが取得できる。
  • しかしモバイルアプリから直接Notionのサーバーに送るのはセキュリティ的にNG💦

    (アクセストークンの発行に必要な機密情報をモバイルアプリに持たせるのを避けたい)

結論
  • AWS Lambdaなどのサービスを介してアクセストークンを発行する。

    (無料枠でOK)

💡
これらの7ステップを1つずつ解説していく。

準備

事前に準備しておくもの
  • スマホアプリのプロジェクト

    今回はReact Native(Expo)を使う

  • Notionアカウント
  • GitHubアカウント
  • AWSアカウント

これから準備するもの
  • Notion API

    後でAPIを発行する。

②③一時トークンをリダイレクトで受け取る

💡
コーディングの都合上、先に②③を作る。

やりたいこと

✅アクセストークンを取得するための準備として、「一時トークン(画像の銀色の鍵)」がほしい。

Image in a image block

必要な作業
  • Notionが発行してくれた「一時トークン」を受け取り、そのままモバイルアプリに渡すだけの簡単なWebページを作る。

    (本当はWebページなしで、直接Notionからモバイルアプリにリダイレクトしたいが、Notionの仕様で必ずWebページを介さないといけない💦)

  • Webページからモバイルアプリを開くためにディープリンクを設定する。

【手順1】ディープリンクを準備

「Webページ」から「スマホアプリ」にリダイレクトできるようにディープリンクを準備しておく必要がある。

→「myapp」という名前のディープリンク(カスタムURLスキーム)を設定すればOK。

【手順1-1】ディープリンクを設定する

✅Expoではapp.jsonを編集するだけでカスタムURLスキームが設定できる。

app.json

{
  "expo": {
    "scheme": "myapp",  // ✅任意のURLスキーム
    "android": {
      "intentFilters": [
        {
          "action": "VIEW",
          "data": [{
            "scheme": "myapp"  // ✅任意のURLスキーム(android用)
          }],
          "category": ["BROWSABLE", "DEFAULT"]
        }
      ]
    }
  }
}

【手順2】リダイレクト元のHTMLを作成

✅一時トークンを受け取って、リダイレクトするだけのHTMLを作成する。

Image in a image block

一時トークンの取得の成功/失敗によって、処理を振り分ける。

一時トークンの取得に成功した場合

モバイルアプリにリダイレクトする。

(リダイレクト先のモバイルアプリ画面は後で作る)

Image in a image block

一時トークンの取得に失敗した場合

エラーページにリダイレクトする。

(リダイレクト先のエラーページは後で作る)

Image in a image block

【手順2-1】リダイレクト用のHTML

✅画面はなし。ページを開いたらすぐリダイレクト処理をする。

notion-web/docs/redirects/index.html

(アプリとは別の場所にnotion-webフォルダを新規作成する)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Redirect</title>
  </head>
  <body>
    <script>
      var urlParams = new URLSearchParams(window.location.search);
      var code = urlParams.get("code");
      if (code == null){
        window.location.replace(
	        // ✅ここは適宜書き換える
          "https://自分のGitHubのID.github.io/notion-web/redirects/fallback.html"
        );
      } else {
        window.location.replace(
          "myapp://oauth/callback?code=" + code
        );
      }
    </script>
  </body>
</html>
💡
コピペしてリンク先だけ変えればOK。

【手順3】成功時のリダイレクト先を作成

✅一時トークンの取得に成功したときのリダイレクト先(モバイルアプリの画面)を作成する。

【手順2】で指定したリダイレクト先「myapp://oauth/callback」に対応する画面を作成する。

Image in a image block

【補足】なぜmyapp://〇〇で自分のアプリが開くの?

✅【手順1】でディープリンクを設定したから。

もしこの設定をしていないと「myapp://」で自分のモバイルアプリが開かない。

【手順3-1】成功時の画面

一時トークンの取得に成功したときに表示する画面。

Image in a image block

app/oauth/callback.tsx

(一旦画面だけ作る。処理は④⑦の解説で追記する。)

import { Text, View } from "react-native"
import { useEffect } from "react"
import { useLocalSearchParams, router } from "expo-router"

export default function Page() {
  // -----------------------------------------------------------------
  // 1. Notion API(Public Integration)の一時トークンを受け取る
  // -----------------------------------------------------------------
  // クエリパラメータから一時トークンを取得
  const { code } = useLocalSearchParams<{ code: string }>()

  // -----------------------------------------------------------------
  // 2. Notion API(Public Integration)のアクセストークンを発行する
  // -----------------------------------------------------------------
	// ④、⑦で追記する。
  }, [])

	// 画面は適当でOK(CSSは省略)
  return (
    <View>
      <Text>Notionの連携が完了しました</Text>
    </View>
  )
}
💡
コピペでOK。

【手順3-2】ルーティング

✅作成した画面とパス「/oauth/callback」と紐づける。

💡
今回はExpo Routerを使っていたためルーティングの作業は一切不要だった。

(Expo Routerでは「app/oauth/callback.tsx」というファイルを作ると自動で「/oauth/callback」にルーティングされる)

【手順4】失敗時のリダイレクト先を作成

✅一時トークンの取得に失敗したとき用のエラーページを作成する。

Image in a image block

【手順4-1】エラー用のHTML

一時トークンの取得に失敗したときに表示するWebページ。

(適当でOK)

Image in a image block

notion-web/docs/redirects/fallback.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>認可エラー</title>
</head>
<body>
  <h1>認可に失敗しました</h1>
</body>
</html>
💡
コピペでOK。

【手順5】GitHub PagesでWebページを公開する

✅作った2つのHTMLをGitHub Pagesで公開する。

(とにかくWeb上に公開できればいい。GitHub Pages以外でもOK。)

【手順5-1】Webページ公開

公式ドキュメントのとおりの手順でOK。

(notion-webフォルダをリポジトリにして、GitHub Pagesで公開する。)

①アプリからURLを開く

やりたいこと

「モバイルアプリ」から「Notionのページ選択画面」を開きたい。

Image in a image block

必要な作業
  • 「Notionのページ選択画面」のURLを準備する。

    (Notion APIを発行すればURLも発行される)

  • モバイルアプリに「Notionのページ選択画面」のリンクを設置する。

【手順1】Notion APIを発行する

✅「Notionのページ選択画面」のURLはNotion APIを発行すると取得できる。

APIは2ステップで発行できる!

【手順1-1】一旦Internal integrationを発行する

最終的にはPublic integrationを発行したいが、いきなりは発行できない。

(一旦Internal integrationを発行する必要がある)

  1. Notionの開発者ページに移動し、新しいインテグレーションを作成する。
    Image in a image block

  2. 分かりやすい名前(アプリ名など)を付けて送信ボタンをクリックする。
    Image in a image block

【手順1-2】Public integrationに変更する
  1. 「ディストリビューション」タブに移動して、「パブリックインテグレーションに設定しますか?」をONにする。
    Image in a image block

  2. 下にある「組織情報」を入力する。

    ※ダミーの情報でも登録できる

    Image in a image block

  3. 下にある「リダイレクトURI」を入力する。

    ※②③の手順で作ったWebページ(GitHub Pages)のURI「https://自分のGitHubのID.github.io/notion-web/redirects/index.html」を入力する。

    Image in a image block

  4. 機密情報3点をメモしておく。
    Image in a image block

【手順2】モバイルアプリの画面にリンクを設置する

✅モバイルアプリから「Notionのページ選択画面」を開くためリンクを設置する。

Image in a image block

【手順2-1】URLを確認する

✅Notion APIの管理画面で確認できる。

Image in a image block

【手順2-2】リンクを設置する

✅適当な画面にリンクを設置する。

app/index.tsx

(CSSは省略)

import { StyleSheet, Text, View } from "react-native"
import { A } from "@expo/html-elements"

export default function Page() {
  return (
    <View>
      <A href="https://api.notion.com/v1/oauth/authorize?client_id=〇〇&response_type=code&owner=user&redirect_uri=〇〇">
        Notionと連携
      </A>
    </View>
  )
}

⑤⑥アクセストークンを取得するAPIを作成

やりたいこと

✅安全にアクセストークンを取得したい。

Image in a image block

必要な作業
  • AWS Lambdaで「アクセストークン」を取得するAPIを作る。

【補足】AWS Lambdaを使わずモバイルアプリでアクセストークンを取得してはだめ?

✅可能だが、セキュリティの観点で非推奨。

  • アクセストークンの取得にはシークレット情報が必要。
  • しかしシークレット情報をモバイルアプリに持たせるのはセキュリティ的に非推奨。
  • シークレット情報をモバイルアプリに持たせないためにAWS Lambdaを使っている。

【手順1】AWS Lambdaで関数を作成

アクセストークンを取得する関数(API)をAWS Lambdaで作成する。

Image in a image block

【手順1-1】AWS Lambdaを開く

AWS Lambda

(AWSのアカウントが無い場合は新規作成する)

(クレジットカードの登録が必要だが今回の用途であれば無料枠で収まる想定)

【手順1-2】関数を新規作成する
Image in a image block

今回はgetNotionAccessTokenという名前の関数にする。

Image in a image block

【手順1-3】コードを貼り付けてデプロイ
Image in a image block

index.mjs(このままコピペでOK!)

/* global fetch */
export const handler = async (event) => {
  let response;
  // 環境変数をチェック
  check_env();
  
  // 環境変数を取得
  const clientId     = process.env.OAUTH_CLIENT_ID;     // NotionのOAuthクライアントID
  const clientSecret = process.env.OAUTH_CLIENT_SECRET; // NotionのOAuthクライアントシークレット
  const redirectUri  = process.env.REDIRECT_URI;        // Notionで設定したリダイレクトURI

  // POST送信されたリクエストボディから
  // 「アクセストークンを受け取るための一時トークン」を受け取る
  const body = JSON.parse(event.body);
  const code = body.code;
  
  // 認可用の文字列を生成
  const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
  
  // Notionからアクセストークンを取得
  let data = await fetch("https://api.notion.com/v1/oauth/token", {
    method: "POST",
    headers: {
      Authorization: `Basic ${credentials}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      grant_type: "authorization_code",
      code: code,
      redirect_uri: redirectUri.toString(),
    }),
  });
  
  // アクセストークンをJSON形式に変換
  data = await data.json();
  
  // エラーがある場合
  if (data.error) {
    response = {
      statusCode: 400,
      body: data,
    };
  }
  // 正常な場合
  else{
    response = {
      statusCode: 200,
      body: data,
    };
  }
  
  return response;
};

/*
 * 環境変数が設定済みかチェック
 * 1つでも未設定なら例外をスローする
 */
function check_env(){
  if (!process.env.OAUTH_CLIENT_ID) {
    throw "OAUTH_CLIENT_ID is required.";
  }
  if (!process.env.OAUTH_CLIENT_SECRET) {
    throw "OAUTH_CLIENT_SECRET is required.";
  }
  if (!process.env.REDIRECT_URI) {
    throw "REDIRECT_URI is required.";
  } 
}

【手順2】設定の変更(環境変数)

3つの機密情報OAUTH_CLIENT_IDOAUTH_CLIENT_SECRETREDIRECT_URIを設定する。

【補足】セキュリティを強化する場合はSecret Managerを使う

【手順2-1】環境変数を設定

[設定ー環境変数]で環境変数を3つ設定する。

Image in a image block

OAUTH_CLIENT_IDの確認方法

Notion API管理画面で発行した「OAuthクライアントID」

Image in a image block

OAUTH_CLIENT_SECRETの確認方法

Notion API管理画面で発行した「OAuthクライアントシークレット」

Image in a image block

REDIRECT_URIの確認方法

Notion API管理画面で登録した「リダイレクトURI」

(今回はGitHub PagesのURI)

Image in a image block

【手順3】設定の変更(タイムアウト)

タイムアウトする時間を30秒に変更する。

【手順3-1】タイムアウトの設定

[設定ー一般設定]で環境変数を設定できる。

Image in a image block

④⑦アクセストークンを取得する

やりたいこと

アクセストークンを取得して、「モバイルアプリ」にアクセストークンを保存したい。

Image in a image block

必要な作業
  • アクセストークンを取得する処理を作る。
    【補足】アクセストークンを取得する流れ

    ✅AWS Lambdaに「一時トークン」をPOST送信すればOK!

    fetch(…)で「一時トークン」をPOST送信する。

    Image in a image block

    fetch(…)のレスポンスからアクセストークンを取り出す。

    Image in a image block

  • アクセストークンを保存する処理を作る。

【手順1】アクセストークン取得、保存

✅③で作った画面に、アクセストークンを取得、保存する処理を追加する。

(画面の見た目は変更なし。処理だけ追記する。)

Image in a image block

【手順1-1】パッケージをインストール

パッケージ「expo-secure-store」をインストールする。

npx expo install expo-secure-store

【補足】expo-secure-storeとは

expo-secure-storeはキーストアを使うためのパッケージ。

アクセストークンはシークレットな情報なので、キーストアなどの安全な場所に保存しておくといい😊

今回のようにExpoを使っている場合は、「Expo SecureStore」を使えば簡単にキーストアにアクセストークンを保存できる

await SecureStore.setItemAsync("notion-token", data.access_token);

(Expo以外の開発環境の場合は各自でキーストアの使い方を調べてください)

【手順1-2】アクセストークンを取得、保存する処理を追記

✅③で作った画面に処理を追記する。

app/oauth/callback.tsx

import { Text, View } from "react-native"
import { useEffect } from "react"
import { useLocalSearchParams, router } from "expo-router"

// npx expo install expo-secure-storeでインストールしておく
import * as SecureStore from "expo-secure-store"

/**
 * アクセストークンを発行するページ
 *
 * 1. Notion API(Public Integration)の一時トークンを受け取る
 * 2. Notion API(Public Integration)のアクセストークンを発行する
 */
export default function Page() {
  // -----------------------------------------------------------------
  // 1. Notion API(Public Integration)の一時トークンを受け取る
  // -----------------------------------------------------------------
  // クエリパラメータから一時トークンを取得
  const { code } = useLocalSearchParams<{ code: string }>()

  // -----------------------------------------------------------------
  // 2. Notion API(Public Integration)のアクセストークンを発行する
  // -----------------------------------------------------------------
  // ✅追記
  useEffect(() => {
    // 一時トークンが取得できない場合はエラーページ(別途作成しておく)にリダイレクト
    if (typeof code === "undefined") {
      router.replace("/oauth/error")
    }
    
    // AWS Lambdaを介してNotionのアクセストークンを取得
    fetch("【AWS Lamgdaの関数URL】", {
      method: "POST",
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        code: code,
      }),
    }).then(async res => {
	    // レスポンスが返ってきたとき
      res.json().then(async data => {
        if (data.error) {
          throw new Error(data.error)
        }

        // アクセストークンを保存
        await SecureStore.setItemAsync("notion-token", data.access_token);
      }
      ).catch(error => {
        throw new Error(error)
      })
    });
  }, [])

	// 画面は適当でOK(CSSは省略)
  return (
    <View>
      <Text>Notionの連携が完了しました</Text>
    </View>
  )
}

💡
これでPublic Integrationの解説は終了。

アプリ利用者の端末でNotion APIが使えるようになった!

【おまけ】Notion APIを実行する

API実行の流れ
Image in a image block

やりたいこと

✅取得したアクセストークンを使って、自由にNotion APIが使いたい!

※今回はNotion APIを使ってこのNotionデータベースからページ一覧(今回は1ページだけ)を取得してみる。

Image in a image block

動作イメージ

データベースを1つ選択し、OKボタンを押すと、「タイトル」と「タグ」が表示される。

Image in a image block

ポイント
  • APIを使うにはアクセストークンが必要。
  • 保存しておいたアクセストークンを取り出せば自由にAPIが使える。

結論
  • アクセストークンさえ手に入れば簡単にNotion APIが使える。

必要な作業
  • 簡単な画面を作る。

  • データベースの一覧を取得してセレクトボックスに表示する。
    Image in a image block

  • OKボタンをタップしたときに、ページ一覧を取得して画面に表示する。
    Image in a image block

【手順1】画面を作成

Notionデータベースを選択し、ページ一覧を表示する画面を作る。

【手順1-1】セレクトボックスのパッケージをインストール

パッケージ「@react-native-picker/picker」をインストールする。

npm install @react-native-picker/picker --save

【手順1-2】画面を作成

✅一旦APIを使った処理はなしで画面だけ作る

任意の画面.tsx(コピペでOK)

import { useEffect, useState } from "react";
import { Button, Text, View } from "react-native";
import * as SecureStore from "expo-secure-store";
import { Picker } from "@react-native-picker/picker";
import { PickerItemProps } from "@react-native-picker/picker/typings";

/**
 * Notionデータベースを1つ選択して、ページ一覧を取得する画面
 */
export function SelectScreen() {
  const [notionDatabases, setNotionDatabases] = useState<PickerItemProps[]>([]); // Notionデータベースのリスト(アクセス許可済み)
  const [selectedNotionDatabaseID, setSelectedNotionDatabaseID] = useState(""); // 選択したNotionデータベースのID
  const [notion, setNotion] = useState<any>(null); // Notion APIのクライアント
  const [pageList, setPageList] = useState<any[]>([]); // ページのリスト

  /**
   * 初期化
   */
  useEffect(() => {
		// 【手順2-2】で作る
  }, []);

  /**
   * ボタンが押されたときの処理
   */
  function ok() {
		// 【手順2-3】で作る
  }

  return (
    <View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
      <Text>データベースを選択</Text>
      <View
        style={{
          flexDirection: "row",
          alignItems: "center",
          marginTop: 8,
          marginBottom: 32,
          columnGap: 32,
        }}
      >
        <Picker
          style={{ width: 300, backgroundColor: "#f0f0f0" }}
          selectedValue={selectedNotionDatabaseID}
          onValueChange={(itemValue, itemIndex) =>
            setSelectedNotionDatabaseID(itemValue)
          }
        >
          {notionDatabases.map((database) => (
            <Picker.Item
              key={database.value}
              label={database.label}
              value={database.value}
            />
          ))}
        </Picker>
      </View>

      <View
        style={{
          flexDirection: "row",
          alignItems: "center",
          justifyContent: "center",
          columnGap: 32,
        }}
      >
        <Button title="OK" onPress={ok} />
      </View>
      {pageList.map((page, index) => (
        <View key={index}>
          <Text>Title: {page.title}</Text>
          <Text>Tags: {page.tags}</Text>
        </View>
      ))}
    </View>
  );
}

【手順2】APIを使う
【手順2-1】Notion APIのパッケージをインストール

パッケージ「@notionhq/client」をインストールする。

npm install @notionhq/client

(公式ではJavaScriptのみSDKが用意されている。他の言語はコミュニティがSDKを作っている場合があるのでSDKがあるか調べてみるといい!)

【手順2-2】データベースの一覧を取得

Notion APInotion.search(…)を使ってデータベースの一覧を取得する。

先ほどのコードのuseEffect()に追記。(コピペでOK)

/**
 * Notionデータベースを1つ選択して、ページ一覧を取得する画面
 */
export function SelectScreen() {
	// 省略
	
  /**
   * 初期化
   */
  useEffect(() => {
	  // ✅追記
    async function init() {
      // アクセストークンを取得
      const access_token = await SecureStore.getItemAsync("notion-token");
      if (!access_token) {
        console.log("アクセストークンが取得できませんでした。");
        return;
      }

      // Notion APIのクライアントを初期化
      const { Client } = require("@notionhq/client");
      setNotion(new Client({ auth: access_token }));

      // Notion APIを利用して、アクセス許可があるすべてのデータベースを取得
      // NOTE:子ページのデータベースも再帰的に含まれる
      const query = ""; // 検索クエリ(データベースのタイトル文字列と比較する)
      const params = {
        query,
        sort: {
          direction: "ascending",
          timestamp: "last_edited_time",
        },
        filter: {
          property: "object",
          value: "database",
        },
        page_size: 100,
      };
      const response = await notion.search(params);
      const databases = response.results; // 検索結果(データベースの配列)
      console.log(databases);

      // データベースを1つずつ処理
      databases.forEach((database: any) => {
        // データベースのタイトルとIDをセット
        setNotionDatabases((prevDatabases) => [
          ...prevDatabases,
          {
            label: database.title[0].plain_text,
            value: database.id,
          },
        ]);
      });

      // 選択状態の初期値を設定
      setSelectedNotionDatabaseID(databases[0] ? databases[0].id : "");
    }

    init();
  }, []);

  return (
		// 省略
  );
}

これでセレクトボックスにアクセス許可済みのデータベースをすべて表示できる。

Image in a image block

【手順2-3】データベースにあるページ一覧を取得

Notion APInotion.databases.query(…)を使ってデータベースにあるページの一覧を取得する。

先ほどのコードのok()に追記。(コピペでOK)

/**
 * Notionデータベースを1つ選択して、ページ一覧を取得する画面
 */
export function SelectScreen() {
	// 省略

  /**
   * ボタンが押されたときの処理
   */
  function ok() {
	  // ✅追記
    async function getPages() {
      // データベースにある全ページを取得(API実行)
      const response_pages = await notion.databases.query({
        database_id: selectedNotionDatabaseID,
      });
      const pages = response_pages.results;

      // ページのタイトルとタグをセット
      setPageList(
        pages.map((page: any) => ({
          title: page.properties.名前.title[0].plain_text,
          tags: page.properties.タグ.multi_select.map((tag: any) => tag.name),
        })),
      );
    }
    getPages();
  }

  return (
		// 省略
  );
}

これでOKボタンをタップすると、選択したデータベースにあるページをすべて表示できる。

(同じ要領でページの作成などもできる)

Image in a image block
Image in a image block

よくある疑問

アクセス許可したページはどうやって取得する?

Public integrationではアクセストークンを発行する過程でユーザーに「アクセス許可するページ」を選択してもらう。

Image in a image block

ここでユーザーが選んだページ(またはデータベース)を取得したい💭

Image in a image block

答え

プログラム側でユーザーが選んだページがどれか?を直接知ることはできない💦

解決方法

そのためユーザーにどのページを使うか?を改めて選択してもらう必要がある。

→ページ(またはデータベース)の選択画面を作成する必要がある。

Image in a image block

まとめ

工程は多いが、1つずつ見ていくとそれほどややこしいことはしていない✨

Public integrationを使うためのポイント

✅とにかくこの7ステップを実装すればPublic integrationが使える!

  • ①②③ 一時トークンを取得して、
    Image in a image block

  • ④⑤⑥⑦ アクセストークンを取得する!
    Image in a image block

今回はこの7ステップを実装するためにGitHub PagesやAWS Lambdaを使った😊

逆にこの7ステップが実装できれば他の方法でもOK👌

参考サイト