Notion APIのデータ構造を実際にAPIを叩きながら理解する

## 背景と実施したこと
Notion APIを活用するにあたって、実際にAPIをたたきつつ公式Reference等でデータ構造の概要を調べましたのでざっくりとまとめて残しておこうと思います。
これから活用しようとしている方の足掛かりになれば幸いです。
※基本的に[公式Referrence](https://developers.notion.com/reference/intro"公式")を参照すれば書いていることです。
　それすら見るのが面倒な人や見てもいまいち理解できなかった人向けということでご了承ください。

なお、本記事を執筆した2022/01/16時点でAPIはBeta版となっています。
試したAPIのverは「2021-08-16」です　※執筆時点では最新

## 目次

1. 概要
1. 事前準備
1. 4つのオブジェクトを理解する
1. まとめ


## 概要
+ 事前準備としてDBの作成、Integrationの用意・権限の付与が必要だよ。
+ DATABASES、PAGES、BLOCKS、USERSのオブジェクトを理解する必要があるよ。
+ PAGES、BLOCKSの違いは少しわかりにくいので要注意だよ。


##事前準備

APIを利用するためには

+ Notion上でのDBの作成
+ Integrationの作成
+ Integrationへ対象のDBをShereする

という3STEPを行う必要がある。

### Notion上でのDBの作成
Notion上のデータ構造は以下のようにDBという箱の中にいくつかのページが含まれるような構造になっているます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/064bc2a1-5ab2-8199-5339-453dfd5f43b6.png)

(DBを作成せずにページのみを作成することもできるが、いろいろと応用が効くので基本的にはDBを作成したうえでページを作ることが多そう)

以下のように空のページ中の「DATABASE」からお好みのパターン（見え方）を選択することでDBが作成されます。
ここではオーソドックスな形のTableを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/42f0effc-90ac-4268-3a35-0083b56760dc.png)

テーブルを作成したのちはお好みで列を整えましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/5f9452ff-b5e9-96c0-3429-aeafc4f82afc.png)


### Integrationの作成
APIを利用するためには自分のユーザとは別にAPIをたたくための箱（ユーザのようなもの）を作成する必要があります。
それがIntegrationです。以下のページからIntegrationを作成しましょう。
[My-Integrationの作成ページ](https://www.notion.so/my-integrations"My-Integration")

作成が無事に終了すると以下のようにTokenが発行されますのでコピーして大事にとっておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/cf4ac60a-8e1a-c9b8-e072-7bd9514d07d7.png)

### Integrationへ対象のDBをShereする
最後にAPIで操作させたいPageを画面からIntegrationしましょう。（これをしておかないとIntegrationからページが見えずAPIも通らない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/728301d9-4621-441c-a224-b1e1aac8195e.png)

## 4つのオブジェクトを理解する
これで準備が整いました。
さっそくAPIをたたきたいところですが、まずはデータ構造について少し解説します。

NotionAPIではDATABASES、PAGES、BLOCKS、USERSの4つのオブジェクトが用意されています、APIを活用するにはこれらを理解する必要があります。

### DATABASE
DATABASESは先ほどもお伝えしたした通りページがたくさん格納されているわけですが、その他にも管理している情報はあります。
図にすると以下のようになりますが、DBの名前そのものや、列として追加した情報に何があるかを「properties」として管理しています。
各ページがそれぞれのpropertiesを持っているというイメージですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/1887097a-7038-e711-77e2-2a8e1569a9f2.png)

実際にPOSTMANでたたいてみましょう。
ヘッダーにBearer Tokenとして先ほど取得したTokenをセットして、APIのVerを「Notion-Version」としてセットします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/89d41357-0a66-e0fb-e6f8-f184e92d62c3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/7bd1c3df-aa7c-e904-66aa-46bc351b13f4.png)

URIは以下のようにセットします。（詳細は公式Referrence参照）
後半のIDは作ったページのURLから取得できます。


ドメイン以下が、「c4723a7c47fa47fe83463fe4a68e6a35?v=b33e6a61682746e8b71fcac9c377914d」
だとした場合パラメータの前（?の前）の部分までがデータベースIDです。

```
https://api.notion.com/v1/databases/c4723a7c47fa47fe83463fe4a68e6a35
```
レスポンスは以下のように帰ってきます

<details><summary>レスポンス</summary><div>

```JSON
{
    "object": "database",
    "id": "c4723a7c-47fa-47fe-8346-3fe4a68e6a35",
    "cover": null,
    "icon": null,
    "created_time": "2022-01-15T13:10:00.000Z",
    "last_edited_time": "2022-01-16T15:34:00.000Z",
    "title": [
        {
            "type": "text",
            "text": {
                "content": "DB-sample",
                "link": null
            },
            "annotations": {
                "bold": false,
                "italic": false,
                "strikethrough": false,
                "underline": false,
                "code": false,
                "color": "default"
            },
            "plain_text": "DB-sample",
            "href": null
        }
    ],
    "properties": {
        "タグ": {
            "id": "I%7Bwm",
            "name": "タグ",
            "type": "multi_select",
            "multi_select": {
                "options": [
                    {
                        "id": "75ee1902-e0d8-4ced-abe0-bc94e2b7a5ff",
                        "name": "仕事",
                        "color": "orange"
                    }
                ]
            }
        },
        "電話番号": {
            "id": "QJiY",
            "name": "電話番号",
            "type": "number",
            "number": {
                "format": "number"
            }
        },
        "BLOG": {
            "id": "%5DzsQ",
            "name": "BLOG",
            "type": "url",
            "url": {}
        },
        "作成日時": {
            "id": "mW_F",
            "name": "作成日時",
            "type": "created_time",
            "created_time": {}
        },
        "タイトル": {
            "id": "title",
            "name": "タイトル",
            "type": "title",
            "title": {}
        }
    },
    "parent": {
        "type": "workspace",
        "workspace": true
    },
    "url": "https://www.notion.so/c4723a7c47fa47fe83463fe4a68e6a35"
}
```
</div></details>

propertiesの部分に設定した各列の値が入っていることがわかるかと思います。


### PAGES、BLOCKS
次にPAGESとBLOCKSですが、図で示すと以下のよう名イメージです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635205/f041805a-1d49-a6dd-ff10-9b17eb640dea.png)

ページの１つである「自己紹介ページ」を例にして解説します。
Retrieve a pageのAPIを以下のようにたたいてみます。(ページIDはページのURLから取得できる)


```
https://api.notion.com/v1/pages/341b1856d91848c4bcb7a49a7fac7fcc
```
<details><summary>レスポンス</summary><div>

```JSON
{
    "object": "page",
    "id": "341b1856-d918-48c4-bcb7-a49a7fac7fcc",
    "created_time": "2022-01-16T14:15:00.000Z",
    "last_edited_time": "2022-01-16T14:42:00.000Z",
    "cover": null,
    "icon": null,
    "parent": {
        "type": "database_id",
        "database_id": "c4723a7c-47fa-47fe-8346-3fe4a68e6a35"
    },
    "archived": false,
    "properties": {
        "タグ": {
            "id": "I%7Bwm",
            "type": "multi_select",
            "multi_select": [
                {
                    "id": "75ee1902-e0d8-4ced-abe0-bc94e2b7a5ff",
                    "name": "仕事",
                    "color": "orange"
                }
            ]
        },
        "電話番号": {
            "id": "QJiY",
            "type": "number",
            "number": 90
        },
        "BLOG": {
            "id": "%5DzsQ",
            "type": "url",
            "url": "http://..."
        },
        "作成日時": {
            "id": "mW_F",
            "type": "created_time",
            "created_time": "2022-01-16T14:15:00.000Z"
        },
        "タイトル": {
            "id": "title",
            "type": "title",
            "title": [
                {
                    "type": "text",
                    "text": {
                        "content": "自己紹介ページ",
                        "link": null
                    },
                    "annotations": {
                        "bold": false,
                        "italic": false,
                        "strikethrough": false,
                        "underline": false,
                        "code": false,
                        "color": "default"
                    },
                    "plain_text": "自己紹介ページ",
                    "href": null
                }
            ]
        }
    },
    "url": "https://www.notion.so/341b1856d91848c4bcb7a49a7fac7fcc"
}
```
</div></details>

感覚としては、略歴など実際のページのコンテンツなども返却されると思いますが、ページの上部で記載されている情報（DB作成時に設定した列の情報）しか返ってきません。

実際のコンテンツを取得するには以下のように、Retrieve block childrenのAPIをたたく必要があります。

```
https://api.notion.com/v1/blocks/341b1856d91848c4bcb7a49a7fac7fcc/children
```
<details><summary>レスポンス</summary><div>

```JSON
{
    "object": "list",
    "results": [
        {
            "object": "block",
            "id": "2357b0a9-ba57-46e3-b641-542e98073722",
            "created_time": "2022-01-16T14:15:00.000Z",
            "last_edited_time": "2022-01-16T14:16:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "heading_2",
            "heading_2": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "略歴",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "略歴",
                        "href": null
                    }
                ]
            }
        },
        {
            "object": "block",
            "id": "827cad51-3afc-4cac-89cd-08376b4fc48b",
            "created_time": "2022-01-16T14:16:00.000Z",
            "last_edited_time": "2022-01-16T14:42:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "paragraph",
            "paragraph": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "XX大学卒\nXX商社で3年働く",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "XX大学卒\nXX商社で3年働く",
                        "href": null
                    }
                ]
            }
        },
        {
            "object": "block",
            "id": "7cbd04c4-d9b3-47d7-924d-919582fb64d4",
            "created_time": "2022-01-16T14:15:00.000Z",
            "last_edited_time": "2022-01-16T14:15:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "paragraph",
            "paragraph": {
                "text": []
            }
        },
        {
            "object": "block",
            "id": "02c24b06-4412-415e-b3ab-34cfd6f47314",
            "created_time": "2022-01-16T14:15:00.000Z",
            "last_edited_time": "2022-01-16T14:16:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "heading_2",
            "heading_2": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "趣味",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "趣味",
                        "href": null
                    }
                ]
            }
        },
        {
            "object": "block",
            "id": "87c8467b-783f-4d47-bf7c-e7935eb169da",
            "created_time": "2022-01-16T14:15:00.000Z",
            "last_edited_time": "2022-01-16T14:17:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "bulleted_list_item",
            "bulleted_list_item": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "ガンプラ作り",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "ガンプラ作り",
                        "href": null
                    }
                ]
            }
        },
        {
            "object": "block",
            "id": "e41cbbc6-ce6b-4947-b3d4-72eeab433067",
            "created_time": "2022-01-16T14:17:00.000Z",
            "last_edited_time": "2022-01-16T14:17:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "bulleted_list_item",
            "bulleted_list_item": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "切手集め",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "切手集め",
                        "href": null
                    }
                ]
            }
        },
        {
            "object": "block",
            "id": "1cb0aee1-23e7-4ff8-bb9d-d8c07fb75611",
            "created_time": "2022-01-16T14:15:00.000Z",
            "last_edited_time": "2022-01-16T14:15:00.000Z",
            "has_children": false,
            "archived": false,
            "type": "heading_2",
            "heading_2": {
                "text": [
                    {
                        "type": "text",
                        "text": {
                            "content": "仕事",
                            "link": null
                        },
                        "annotations": {
                            "bold": false,
                            "italic": false,
                            "strikethrough": false,
                            "underline": false,
                            "code": false,
                            "color": "default"
                        },
                        "plain_text": "仕事",
                        "href": null
                    }
                ]
            }
        }
    ],
    "next_cursor": null,
    "has_more": false
}
```
</div></details>

BLOCKに関する説明は公式ドキュメントによると以下の通りです。
> A block object represents content within Notion. Blocks can be text, lists, media, and more. A page is a type of block, too!
> ブロックオブジェクトは、Notion内のコンテンツを表します。ブロックは、テキスト、リスト、メディアなど、さまざまなものがあります。**ページもブロックの一種です。**

つまり先ほどのリクエストはページというブロックに対してRetrieve block childrenのAPIを投げたということになります。
※ページIDはブロックIDとしても使える。
この点が少しわかりにくい点かもしれませんね。

### USERS
USERSはそんなに難しいことはないです。
特筆すべき点としてはIntegrationがuserオブジェクトに含まれていることくらいでしょうか。

```
https://api.notion.com/v1/users
```

<details><summary>レスポンス</summary><div>

```JSON
{
    "object": "list",
    "results": [
        {
            "object": "user",
            "id": "1f6bac22-8e2f-4d4b-851d-e70d45200d10",
            "name": "XXXXXX",
            "avatar_url": "XXXXXX",
            "type": "person",
            "person": {
                "email": "XXXXXX@XXXXXX.com"
            }
        },
        {
            "object": "user",
            "id": "4bfe13a7-a184-4635-b8e1-340c4af21cd1",
            "name": "MyIntegration",
            "avatar_url": null,
            "type": "bot",
            "bot": {
                "owner": {
                    "type": "workspace",
                    "workspace": true
                }
            }
        }
    ],
    "next_cursor": null,
    "has_more": false
}
```
</div></details>

## まとめ
ということで今回はNotion APIを触りながら理解する記事でした。
以下の重要な点をおさらいしておきましょう。

+ 事前準備としてDBの作成、Integrationの用意・権限の付与が必要だよ。
+ DATABASES、PAGES、BLOCKS、USERSのオブジェクトを理解する必要があるよ。
+ PAGES、BLOCKSの違いは少しわかりにくいので要注意だよ。

今回はGETメソッドのAPIしか触れていませんが、他にもAPIの種類もあります。
Beta版ということでそこまで豊富というわけではないようですが、そのあたりも今後触れていき学びがあればシェアしていきたいと思います。