😎

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データベース意外と何でもできる~ 便利~

Discussion