Astro製のサイトでNotion APIにアクセスして、データベースのデータを取得する

# 背景

Qiita Engineer Festa 2024への参加記録のポータルを自前でつくりたいなあ……

最近使って、少しはわかるAstroでサイトをつくるか……

でもデータベースの立ち上げとか、データを入力するためのフォームをつくるとかやっていると、イベント終了まで終わらないな……

あー、入力しやすいからNotionのデータベースでいいか……

――ということで、Astro製のサイトでNotionのデータベースのデータを表示することをしてみます

具体的には、

- NotionにQiita Engineer Festa 2024の投稿記録データベースを作成します
- Astroで一枚ものサイトを立ち上げます
- Notion APIでデータベースのデータを取得します
- 取得したデータを表示し、投稿記録のスタンプカードと投稿リストを表示します

といったところです

Qiita Engineer Festaならすでにスタンプカードあるじゃんとか言わないでください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/750621f0-eafb-4ee5-cde7-c3ad9ee160e9.png)

知っている知識で何かつくりたくなるのがエンジニアなのです、多分

# 大事なこと

:::note warn
これから書く内容でつくったサイトはインターネット上への公開を前提にしていません

純なAstroコンポーネントでつくるため、ビルド時にNotion APIで取得した内容でサイト内のデータが決まります（その後、Notionデータベースを更新しても、変更はサイトに反映されません）

必要なら、エンドポイントとかVueコンポーネントとか、動的にNotion APIにアクセスして表示するようなそういうあれこれでつくる必要があります
余裕と気力が続けば、これはのちのちに対応したいと思いますが、今回は対応しません
:::

# 記事の構成

一気に記事を書ききる気力がないため、今回はNotion APIからデータを取得するところまでとなります

二つ目の記事がAPIから取得したデータの加工、三つ目の記事がデータの表示となる予定です

# 前準備

では、まずはNotionにデータベースを準備します

項目名は、`Title`（投稿名）、`Published`（投稿日）、`Tags`（おおざっぱなタグ、複数）、`Status`（投稿状況）、`Url`（投稿のURL）とします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/b074f45d-72f8-a7d9-2c73-b1cedfc46ebe.png)

:::note info
項目名を日本語にすると、Notion APIでデータを取得したとき、キー名が日本語になって気持ち悪かったので、アルファベットでリネームしました
（リネームすると、APIから取得できるデータが即時書きかわりました　反応が良すぎて気味悪かったです）
:::

後々の利便性のため、および、目標達成のために、データベースには目標38記事分のデータをあらかじめつくっておき、Statusを`Completed`（投稿済み）、`In_progress`（作業中）、`Not_started`（未着手）として、管理することにしました

また、`Published`（投稿日）は、予定にもとづいて適当な日付を突っ込みました（1日3件書くというアホみたいな予定です）
これも、サイトをつくっていたら、日付が空なデータがあるとちょっと困ったことがあったための対応です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/dd702389-f245-a41b-83e0-1a9c44531062.png)

下準備はこれで完成です

続いて、サイトを作成していきます

# サイト立ち上げ

Astroの公式サイトのGetting Startedから、開始します

https://docs.astro.build/en/install-and-setup/

コマンドを走らせれば、パッケージもろもろ準備してくれます

```
npm create astro@latest
```

<details><summary>実行記録</summary>

```console
> npx
> create-astro


 astro   Launch sequence initiated.

   dir   Where should we create your new project?
         .

  tmpl   How would you like to start your new project?
         Empty

    ts   Do you plan to write TypeScript?
         Yes

   use   How strict should TypeScript be?
         Strict

  deps   Install dependencies?
         Yes

   git   Initialize a new git repository?
         Yes

      ✔  Project initialized!
         ■ Template copied
         ■ TypeScript customized
         ■ Dependencies installed
         ■ Git initialized

  next   Liftoff confirmed. Explore your project!
         Run npm run dev to start the dev server. CTRL+C to stop.
         Add frameworks like react or tailwind using astro add.

         Stuck? Join us at https://astro.build/chat
```

</details>

`npm run dev`します
↓
はい、OKです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/c417d7e4-8e24-290d-ae60-07fe22e42542.png)

<details><summary>実行記録</summary>

```console
> dev
> astro dev

15:48:27 [types] Added src/env.d.ts type declarations.
15:48:27 [vite] Port 4321 is in use, trying another one...

 astro  v4.11.5 ready in 681 ms

┃ Local    http://localhost:4322/
┃ Network  use --host to expose

15:48:27 watching for file changes...
15:49:52 [200] / 13ms
```
</details>

ここで一度、GitHubに入れておきます
`.gitignore`は準備してもらったので、普通にコミット？　プッシュ？　なんだろう、何かして送信すればOKです

# Notionへのアクセス

もう少し下準備が必要でした

Notion APIでアクセスするための設定とトークンの取得です

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/b30ff57e-4c23-8efa-0a4d-1008f8b64dde.png)

Notionの「設定」から「コネクト」で「インテグレーションを作成または管理する」へアクセスします

「新しいインテグレーション」から、

- インテグレーション名：任意の名前を指定
- 関連ワークスペース：準備したデータベースのあるワークスペースを指定
- 種類：「内部」を指定

を設定し、保存します

すると、インテグレーションの管理画面に飛べるので、そこから「内部インテグレーションシークレット」でアクセストークンを取得します（トークンは後ほど使うので控えておきますが、後で必要なタイミングでアクセスして取得しても問題ありません）

「機能」の「コンテンツ機能」は「コンテンツを読み取る」だけで今回はじゅうぶんなので、更新、挿入のデフォルトのチェックを外します

「ユーザー機能」も「ユーザー情報なし」でいいので、設定します

続いて、今作成したインテグレーションから目的のデータベースにアクセスできるようにします

Notionのデータベースを表示し、右上のメニューから、「コネクト」、「接続先」で今作成したインテグレーションを選びます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3821654/fed8a357-f2fa-f0b2-891b-1c5a2631901e.png)

それから、データベースのIDも取得しておきます

IDの取得の仕方は以下のページの"Where can I find my database's ID?"をご参照ください

https://developers.notion.com/docs/working-with-databases

目的のデータベースをフルページで開いたときのURLが`https://www.notion.so/{workspace_name}/{database_id}?v={view_id}`
となっていますので、それで取得できます

はい、これで下準備は本当に終わりです

# AstroでNotion APIにアクセスする

Astroのサイト開発に戻ります

今回は一枚もののページですし、スピード重視で`src/pages/index.astro`にべた書きしていきます（普通はコンポーネントわけしたほうがいいです）

Notion APIにはAPIアクセス向けのクライアントのパッケージが存在するので、それを導入します

https://www.npmjs.com/package/@notionhq/client

```
npm install @notionhq/client
```
<details><summary>実行結果</summary>

```
added 14 packages, and audited 436 packages in 4s

165 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```
</details>

---

まずは、アクセスしてみます

:::note alert
以下はエラー処理とか何も考えていないコードです

IDやトークンもべた書きしているので、決して真似してはいけません
PublicなGitHubリポジトリとかに投稿してしまった場合は、即時どうにかして削除しましょう
:::

```js
---
import { Client } from "@notionhq/client";

const database_id = "********";
const auth_key = "********";

const notion = new Client({ auth: auth_key });
const data = await notion.databases.query({	database_id });
const articles = data.results;

console.log(articles);
---

<html lang="en">
	<head>
		<meta charset="utf-8" />
		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
		<meta name="viewport" content="width=device-width" />
		<meta name="generator" content={Astro.generator} />
		<title>Astro</title>
	</head>
	<body>
		<h1>Astro</h1>
	</body>
</html>
```

これで以下のようなデータを取得できることが確認できました

<details><summary>console.logの表示</summary>

```
[
  {
    object: 'page',
    id: '*',
    created_time: '2024-07-05T05:13:00.000Z',
    last_edited_time: '2024-07-05T05:13:00.000Z',
    created_by: { object: 'user', id: '*' },
    last_edited_by: { object: 'user', id: '*' },
    cover: null,
    icon: null,
    parent: {
      type: 'database_id',
      database_id: '*'
    },
    archived: false,
    in_trash: false,
    properties: {
      Status: [Object],
      Tags: [Object],
      Url: [Object],
      Published: [Object],
      Title: [Object]
    },
    url: 'https://www.notion.so/*',
    public_url: null
  },
  {
    object: 'page',
    id: '*',
    created_time: '2024-07-05T05:13:00.000Z',
    last_edited_time: '2024-07-05T05:13:00.000Z',
    created_by: { object: 'user', id: '*' },
    last_edited_by: { object: 'user', id: '*' },
    cover: null,
    icon: null,
    parent: {
      type: 'database_id',
      database_id: '*'
    },
    archived: false,
    in_trash: false,
    properties: {
      Status: [Object],
      Tags: [Object],
      Url: [Object],
      Published: [Object],
      Title: [Object]
    },
    url: 'https://www.notion.so/*',
    public_url: null
  },
  (略)
```
</details>

ここまで来れば、後はこれらのデータを適当に加工して、表示するだけです

しかし、APIで取得してきたデータはプロパティが多くて扱いづらいので、必要なデータを抜き取ったデータをつくったほうがよさそうでした
ということで、次は「データの加工」でおこなったことを記事にします

# 後書き

この手の開発は瞬発力が求められるのでしょうが、知識不十分な素人のため、調査しながらだと、なかなか思うように進みません
上記くらいの内容で、半日くらいかかりました

これからの課題は、
- fetchの適切なエラー処理

だと思っていますので、少しずつ調べていきます