😎

NextJS(AppRouter)で叩くNotion Api

2024/02/29に公開

目的

ホームページを作成する際に、ページの更新をしていくのは開発者だけでなく、一般の人(コードが書けない人)も更新ができるようになれば、開発側の負担軽減、ページの即時更新につながる。。そのため誰でも編集が可能である notion を利用することにした。

実際のコード

https://github.com/nagisa599/notion-api-study

使用技術

  • notionhq/client(2.2.14)
  • next(14.0.4)
  • react(18),
  • notion-to-md (3.1.1),
  • react-markdown (9.0.1)
  • remark-gfm(4.0.0)

事前準備

notion の事前準備

integrations の設定

https://www.notion.so/my-integrations にアクセスをし、インテグレーションの作成を行う
インテグレーションの作成を行うとシークレットキーが得られる。またこのシークレットキーは api を叩く際に必要なためメモをしとくこと。

データベースの作成と設定

  1. notion でデータベースを作成
  2. 右上の 3 点リーダを選択
  3. 接続先を選択
  4. intergations で設定したプロジェクト名を選択

データベースの作成と設定

作成したデータベースの url(xxx の部分)をメモをする。

https://www.notion.so/xxxxx?v=yyyyy

NextJs の事前準備

NextJs のフレームワークをインストール

npx create-next-app@latest --typescrip

パッケージをインストール

@notionhq/clint は、Notion データベースのクエリを実行してデータを取得するパッケージである。
notion-to-md は,Notion の本文を取得し、MarkDown 書式で生成してくれるパッケージである。
イメージとしては、Notion のテーブル、メタデータを取得したければ、@notionhq/clint を利用し、本文を取得したければ、notion-to-md を利用する感じである。

npm i @notionhq/client notion-to-md

.env ファイルに記述

.env ファイルをプロジェクト直下に記述。notion で取得したシークレットきーと url(xxxx の部分)をそれぞれ env ファイルに記載する。

NEXT_PUBLIC_NOTION_API_KEY = "secret_aaaaaaa"
NEXT_PUBLIC_DB_URL = "xxxx"

NotionClient を作成

import { Client } from "@notionhq/client";

// Notion APIのクライアントを作成
export const notion = new Client({
  auth: process.env.NEXT_PUBLIC_NOTION_API_KEY,
});

フロントのコードの作成

import React from "react";
import { headers } from "next/headers";
const page = async () => {
  const host = headers().get("host");
  // テーブルデータの取得
  const responseTableData = await fetch(`http://${host}/api/notion`);
  const tableData = await responseTableData.json();

  // ページデータの取得
  const detailId = "aifhaifhaie;hfaiehfae;hfae"; //ページID(テーブルを取得する際にメタデータとしてidが得られる
  const responsePageData = await fetch(`http://${host}/api/notion/${detailId}`);
  const Data = await responsePageData.json();

  return <div></div>;
};

export default page;

実際のコード

メタデータ、テーブルデータの取得

import { notion } from "@/utils/notionClient";
import { NextResponse } from "next/server";
const corsHeaders = {
  "Access-Control-Allow-Origin": "*", // すべてのオリジンからのアクセスを許可
  "Access-Control-Allow-Methods": "GET", // 許可されるHTTPメソッド
  "Access-Control-Allow-Headers": "Content-Type", // 許可されるヘッダー
  "Content-Type": "application/json",
};

export async function GET() {
  try {
    const databaseId = process.env.NEXT_PUBLIC_DB_URL || "DEFAULT_DATABASE_ID";
    const notionResponse = await notion.databases.query({
      database_id: databaseId,
    });
    //実際にフロントに返すデータを決める。
    const Response = await Promise.all(
      notionResponse.results.map(async (result: any) => {
        const id = result.id; // eslint-disable-line
        const title = result.properties.name.title[0].plain_text; // eslint-disable-line
        return { id, title };
      })
    );
    return new NextResponse(JSON.stringify(Response), {
      headers: corsHeaders,
      status: 200,
    });
  } catch (error) {
    console.error(error); // エラーをコンソールに出力
    return new NextResponse(
      JSON.stringify({ error: "Internal Server Error" }),
      {
        headers: corsHeaders,
        status: 500,
      }
    );
  }
}

notionResponse の中身としては、以下の写真の通りとなる。テーブルの中身だけでなく、そのテーブルの作成日、更新日、更新者などメタデータも取得ができる。実際にテーブルデータは、properties に入っている。

{
  object: 'list',
  results: [
    {
      object: 'page',
      id: 'xxxxxx',
      created_time: '2024-02-25T01:24:00.000Z',
      last_edited_time: '2024-02-25T14:52:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      cover: null,
      icon: null,
      parent: [Object],
      archived: false,
      properties: [Object],
      url: 'https://www.notion.so/yyyyy',
      public_url: null
    },
    {
      object: 'page',
      id: 'xxxx',
      created_time: '2024-02-25T01:24:00.000Z',
      last_edited_time: '2024-02-29T06:38:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      cover: [Object],
      icon: [Object],
      parent: [Object],
      archived: false,
      properties: [Object],
      url: 'https://www.notion.so/yyyy',
      public_url: null
    }
  ],
  next_cursor: null,
  has_more: false,
  type: 'page_or_database',
  page_or_database: {},
  request_id: 'zzzzzzzzz'
}

ページデータの取得

import { NotionToMarkdown } from "notion-to-md";
import { NextResponse } from "next/server";
import { notion } from "@/utils/notionClient";
const corsHeaders = {
  "Access-Control-Allow-Origin": "*", // すべてのオリジンからのアクセスを許可
  "Access-Control-Allow-Methods": "POST, GET, OPTIONS, DELETE, PUT", // 許可されるHTTPメソッド
  "Access-Control-Allow-Headers": "Content-Type", // 許可されるヘッダー
  "Content-Type": "application/json",
};
export async function GET(
  request: Request,
  { params }: { params: { id: string } }
) {
  try {
    const { id } = params;
    const n2m = new NotionToMarkdown({ notionClient: notion });
    const mdblocks = await n2m.pageToMarkdown(id);
    const mdString = n2m.toMarkdownString(mdblocks);
    const response = mdString.parent;
    return new NextResponse(JSON.stringify(response), {
      headers: corsHeaders,
      status: 200,
    });
  } catch (error) {
    console.error(error); // エラーをコンソールに出力
    return new NextResponse(
      JSON.stringify({ error: "Internal Server Error" }),
      {
        headers: corsHeaders,
        status: 500,
      }
    );
  }
}

実際にデータをフロントエンドに返す際は、Markdown 形式で返している。

補足

  • 関連サービスの紹介
    • react-markdown (9.0.1) (Markdown を HTML に変換するパッケージ)
    • remark-gfm(4.0.0) (Markdwon に css を適応できるようにするパッケージ)
  • 参考文献や、公式ページへのリンクなど
    https://github.com/nagisa599/notion-api-study

おわりに

notion データベース意外と何でもできる~ 便利~

GitHubで編集を提案

Discussion