初めに
NotionAPIはDB(データベース)のようにサクッとデータを取得することが可能で、さらに1レコードに対してノート(記事コンテンツ)も取得できます。ブログ記事を更新する画面実装も必要ないので、手軽にブログサイトを構築できます。ただデータを取得するのに少し癖がありますが。
2025/9/3 からNotionAPIの仕様が変更されたので、変更バージョンで説明していきます。
Notoin画面でDBとテーブルを作成
Notionの登録(無料)が必要になります。
https://www.notion.com/ja
1、チームスペース作成
ログイン後、新規チームスペースを作成します。プライベートでも可能ですが、今回はチームスペースで作成します↓
今後の権限などを考えクローズドで作成します。1人の場合、オープンまたはデフォルトでも大丈夫です。名前は任意になります↓
2、チームスペース内で新規ページ追加(DBやテーブル的なもの)
以下のように表示されるので、テーブル名や項目を設定していきます↓

今回はブログを想定しているので、以下の項目で設定していきます↓
Title
Slug テキスト
Category 選択(例:カテゴリ1,カテゴリ2,カテゴリ3)
Status 選択(draft,published,private)
PublishedAt 時間(公開日)
UpdatedAt 時間(更新日)
Description テキスト
任意のテーブル名「blog」を入力し、項目に値を追加したイメージは以下になります↓
コンテンツ(ノート)を作成する場合は、以下のようにTitle項目の値にカーソルを当て「開く」をクリック↓
コンテンツ(ノート)画面になるので、こちらでメイン画像(ヘッダー画像)や記事内容を記載していきますが、コンテンツ取得まで説明すると長くなるので省略します(今回は項目の値を取得するまで説明します)↓
3、インテグレーション作成
レコードも作成できたので、次は以下のURLでインテグレーションを作成します(APIキー作成)↓
https://www.notion.so/profile/integrations
インテグレーション名は任意で、作成したアカウントのワークスペースを選択、種類は内部にして、保存します↓

インテグレーション設定を押下します↓
デフォルト設定はAPIでの更新・挿入・ユーザー情報を読み取る権限があるので今回はなしにします。必要に応じて機能から設定してください。また内部インテグレーションシークレットがAPIキーになるので、後ほど.envに設定していきます↓
次に、アクセスからAPIキーとチームスペースを紐づけします。アクセス権限を編集を押下します↓

先ほど作成したチームスペース(データベース)を選択して保存します↓

これでNotion側の設定が終わったので、次はHonoを利用してAPIを実装していきます。
Hono実装
今回はnpmでプロジェクトを構築するので、nodeのインストールが事前に必要です(nodeインストール手順は省略します)。
1.プロジェクト構築
以下のコマンドでプロジェクト(notion-api)を作成し、必要なライブラリをインストールします↓
npm create hono@latest notion-api
✔ Which template do you want to use? cloudflare-workers
✔ Do you want to install project dependencies? Yes
✔ Which package manager do you want to use? npm
npm install @notionhq/client
npm install --save-dev @cloudflare/workers-types
Honoとは関係ないですが、以下もインストールします(一度だけ使用するスクリプトファイル用)↓
# dotenv本体をインストール
npm install dotenv
# Node.jsの型定義(pathやprocessを使うために必要)を開発用としてインストール
npm install --save-dev @types/node
2.tsconfig.jsonに"types" 追記
console.logを使用するために"types"を追記します↓
{
"compilerOptions": {
"target": "ESNext",
・・・省略
"types": ["@cloudflare/workers-types"],
"jsx": "react-jsx",
"jsxImportSource": "hono/jsx"
}
}
3.env作成
NOTION_DATA_SOURCE_IDは後ほど取得するので、一旦空にします↓
# Notion API Configuration
# API キー(Integration の設定ページで取得)
NOTION_API_KEY=XXXX
# データベース ID(32文字のID)
# データベース URL の https://notion.so/xxxxx の部分(クエリパラメータより前)
NOTION_DATABASE_ID=XXXX
# データソース ID(API v2025-09-03 以降で必要)
# 取得方法: npm run get-datasource-id を実行
NOTION_DATA_SOURCE_ID=
# APIのトークン
SERVICE_SECRET=test
NOTION_DATABASE_IDは以下の青い部分になります↓
(※水色の部分はIDではないので注意が必要です)
4.NOTION_DATA_SOURCE_IDを取得するファイルを作成
NOTION_DATABASE_IDを使用して、NOTION_DATA_SOURCE_IDを取得します。
そのため取得は一度のみ行います(以後、.envに設定すれば取得する必要がないため)
-
NOTION_DATABASE_ID→NOTION_DATA_SOURCE_IDを取得するために使用 -
NOTION_DATA_SOURCE_ID→ 全レコードを取得するために使用
以下がNOTION_DATA_SOURCE_IDを取得するスクリプトになります↓
/**
* Notion データベース ID からデータソース ID を取得するスクリプト
*
* 使用方法:
* npx tsx scripts/get-datasource-id.ts
*/
import { config } from "dotenv";
import { Client } from "@notionhq/client";
import * as path from "path";
// .env.local ファイルを読み込む
config({ path: path.resolve(process.cwd(), ".env") });
// 環境変数から取得
const NOTION_API_KEY = process.env.NOTION_API_KEY;
const NOTION_DATABASE_ID = process.env.NOTION_DATABASE_ID;
if (!NOTION_API_KEY) {
console.error("❌ NOTION_API_KEY が設定されていません");
process.exit(1);
}
if (!NOTION_DATABASE_ID) {
console.error("❌ NOTION_DATABASE_ID が設定されていません");
process.exit(1);
}
const notion = new Client({
auth: NOTION_API_KEY,
});
async function getDataSourceId() {
try {
console.log("📊 データベース情報を取得中...");
console.log(`データベース ID: ${NOTION_DATABASE_ID}\n`);
// データベース情報を取得
const database = await notion.databases.retrieve({
database_id: NOTION_DATABASE_ID!,
});
console.log("✅ データベース情報:");
console.log(
` タイトル: ${
(database as any).title?.[0]?.plain_text || "(タイトルなし)"
}`
);
// データソース情報を取得
if ("data_sources" in database && Array.isArray(database.data_sources)) {
const dataSources = database.data_sources;
console.log(`\n📁 データソース数: ${dataSources.length}`);
dataSources.forEach((ds: any, index: number) => {
console.log(`\nデータソース ${index + 1}:`);
console.log(` データソース ID: ${ds.id}`);
console.log(` タイプ: ${ds.type || "database"}`);
});
if (dataSources.length > 0) {
const firstDataSource = dataSources[0];
console.log("\n" + "=".repeat(60));
console.log("📝 .env に追加してください:");
console.log("=".repeat(60));
console.log(`NOTION_DATA_SOURCE_ID=${firstDataSource.id}`);
console.log("=".repeat(60));
}
} else {
console.log("\n⚠️ データソース情報が見つかりません");
console.log(
"このデータベースは v2025-09-03 に対応していない可能性があります"
);
}
} catch (error: any) {
console.error("\n❌ エラーが発生しました:");
if (error.code === "object_not_found") {
console.error("\nデータベースが見つかりません。以下を確認してください:");
console.error("1. NOTION_DATABASE_ID が正しいか");
console.error(
"2. Notion インテグレーションがデータベースに接続されているか"
);
} else if (error.code === "unauthorized") {
console.error("\nAPI キーが無効です。NOTION_API_KEY を確認してください");
} else {
console.error(error.message);
console.error("\n詳細:", error);
}
process.exit(1);
}
}
getDataSourceId();
スクリプトを実行します↓
npx tsx scripts/get-datasource-id.ts
Need to install the following packages:
tsx@4.21.0
Ok to proceed? (y) y
[dotenv@17.2.3] injecting env (4) from .env -- tip: ⚙️ enable debug logging with { debug: true }
以下が取得したNOTION_DATA_SOURCE_ID情報になるので.envに張り付けます↓
📊 データベース情報を取得中...
データベース ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX(NOTION_DATABASE_ID)
✅ データベース情報:
タイトル: blog
📁 データソース数: 1
データソース 1:
データソース ID: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX(NOTION_DATA_SOURCE_ID)
タイプ: database
============================================================
📝 .env に追加してください:
============================================================
NOTION_DATA_SOURCE_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX(NOTION_DATA_SOURCE_ID)
============================================================
5.NotionAPI取得ファイル作成
NOTION_DATA_SOURCE_ID を使用してNotionAPI取得実装をしていきます↓
import { Client } from "@notionhq/client";
// 環境変数の型定義(index.tsと共通化してもOK)
export type Bindings = {
NOTION_API_KEY: string;
NOTION_DATA_SOURCE_ID: string;
};
/**
* 環境変数を受け取って Notion Client と ID を返す関数
*/
export const getNotion = (env: Bindings) => {
// --- 環境変数の検証 ---
if (!env.NOTION_API_KEY) {
throw new Error("NOTION_API_KEY が設定されていません");
}
if (!env.NOTION_DATA_SOURCE_ID) {
throw new Error("NOTION_DATA_SOURCE_ID が設定されていません");
}
// --- Notion APIクライアントの初期化 ---
const client = new Client({
auth: env.NOTION_API_KEY,
fetch: (url, init) => {
return fetch(url, init);
}, // Cloudflare Workersのネイティブfetchを明示的に渡します
});
// v2025-09-03 以降対応
const dataSourceId = env.NOTION_DATA_SOURCE_ID;
return {
client,
dataSourceId,
};
};
メインのAPI実装の取得条件(filter)は以下にします↓
- "Title" → 必須
- "Slug" → 必須
- "Category" → 必須
- "PublishedAt" → 必須
- "Status" → "published"(公開のみ)
import { Hono } from "hono";
import { getNotion } from "./client";
// 環境変数の型定義
type Bindings = {
NOTION_API_KEY: string;
NOTION_DATA_SOURCE_ID: string;
SERVICE_SECRET: string;
};
const app = new Hono<{ Bindings: Bindings }>();
// ルートパス(動作確認用)
app.get("/", (c) => {
return c.text("Notion API 起動中");
});
// サンプルデータ取得API
app.get("/api", async (c) => {
// 1. セキュリティチェック
const authHeader = c.req.header("x-service-secret");
if (authHeader !== c.env.SERVICE_SECRET) {
return c.json({ error: "Unauthorized" }, 401);
}
// Cloudflareのエッジサーバーやブラウザに対し「このレスポンスは保存するな」と命令(念のため)
c.header("Cache-Control", "no-store, no-cache, must-revalidate");
const { searchParams } = new URL(c.req.url);
const slug = searchParams.get("slug")!;
// 2. Notion クライアントの初期化
const { client, dataSourceId } = getNotion(c.env);
try {
console.log("Notion API 取得開始");
// 3. Notion API 実行
const data = await client.dataSources.query({
data_source_id: dataSourceId,
filter: {
and: [
{
property: "Title",
title: {
is_not_empty: true,
},
},
{
property: "Slug",
rich_text: slug ? { equals: slug } : { is_not_empty: true },
},
{
property: "Status",
select: {
equals: "published", // 公開記事のみ取得
},
},
{
property: "Category",
select: { is_not_empty: true },
},
{
property: "PublishedAt",
date: {
is_not_empty: true,
},
},
],
},
sorts: [
{
property: "PublishedAt",
direction: "descending",
},
],
});
if (data.results.length === 0) {
return c.json({ error: "Not found", message: "取得記事は0件です" }, 404);
}
// data.results が Notion から返ってきた全レコードの配列
const posts = data.results.map((item: any) => {
const p = item.properties; // 短く書くために変数に入れます
return {
id: item.id, // NotionのページID
// 1. タイトル (Titleプロパティ)
title: p.Title?.title?.[0]?.plain_text ?? "No Title",
// 2. テキスト (Slug, Description) -> rich_text配列の中身を取得
slug: p.Slug?.rich_text?.[0]?.plain_text ?? "",
description: p.Description?.rich_text?.[0]?.plain_text ?? "",
// 3. セレクト (Category, Status) -> selectオブジェクトのnameを取得
category: p.Category?.select?.name ?? null,
status: p.Status?.select?.name ?? null,
// 4. 日付 (PublishedAt, UpdatedAt) -> dateオブジェクトのstartを取得
publishedAt: p.PublishedAt?.date?.start ?? null,
updatedAt: p.UpdatedAt?.date?.start ?? null,
};
});
// 4. JSONを返す
return c.json({
count: posts.length,
results: posts,
fetchedAt: new Date().toISOString(),
});
} catch (error) {
console.error(error);
return c.json({ error: "Notion API Error" }, 500);
}
});
export default app;
6.ローカル確認
npm run dev
取得APIを実行します↓
curl -X GET -H "x-service-secret: test" "http://localhost:8787/api"
{
"count":2,
"results":[
{
"id":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"title":"タイトル2",
"slug":"title2",
"description":"タイトル2の説明",
"category":"カテゴリ3",
"status":"published",
"publishedAt":"2025-12-02",
"updatedAt":"2025-12-02"
},
{
"id":"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"title":"タイトル1",
"slug":"title1",
"description":"タイトル1の説明",
"category":"カテゴリ2",
"status":"published",
"publishedAt":"2025-12-01",
"updatedAt":"2025-12-02"
}
],
"fetchedAt":"2025-12-04T18:11:35.404Z"
}
本番確認(cloudflareにデプロイ)
前提でcloudflareに登録が必要になります。
https://www.cloudflare.com/
デプロイから除外するファイル設定(.wranglerignore作成)していきます(自動で判断して除外されるとは思いますが、念のため記載します)↓
# scriptsフォルダを除外
scripts/
# その他、不要なファイルがあれば
README.md
.env
GITHUBを使用せずに直接デプロイする場合
ターミナルでcloudflareログイン(認証)していきます↓
npx wrangler login
ブラウザに移動すると思いますので、ブラウザでログインして認証します(Allowを押下)↓

デプロイします↓
npm run deploy
デプロイだけでは、API実行時エラーになるので本番の環境変数も設定します↓
(今回はコマンドで環境変数を登録しますが、cloudflare画面からでも環境変数の登録は可能です)
npx wrangler secret put NOTION_API_KEY
npx wrangler secret put NOTION_DATA_SOURCE_ID
npx wrangler secret put SERVICE_SECRET
デプロイ後は、発行されたURL(デプロイ後のログ または cloudflareから確認できます)APIを実行します↓
curl -X GET -H "x-service-secret: test" "https://発行されたURL/api"
GITHUBをつかってデプロイする場合
GITHUBでリポジトリを作成してHonoプロジェクトをpushします(pushまでの説明は省略します)。
GITHUBにプッシュしたら自動で更新されるようにしたい(CI/CD)ので、GITHUB ACTIONSを使用します。
1.CloudflareでAPIトークンを発行する
Cloudflare画面の右上の「プロファイル」→「プロフィール」→「APIトークン」→「トークンを作成する」→ Cloudflare Wokersを編集する「テンプレートを使用する」押下↓
権限から設定していきます。デプロイ時や運用時に『権限不足エラー』で止まらないように、関連機能をまとめて許可したテンプレートの設定になります↓
| 権限名 | レベル | Honoでの必須度 | 説明 |
|---|---|---|---|
| Workers KV Storage | 編集 | 任意 | KV(Key-Valueストア)を使用する場合に必要。使わない場合は不要ですが、あっても問題ありません。 |
| Workers スクリプト | 編集 | 【必須】 | Honoのプログラムコードをアップロード・更新するために必要。これがないとデプロイできません。 |
| Workers ルート | 編集 | 【必須】 | どのURL(例: api.example.com/*)でAPIを動かすかを設定するために必要。 |
| アカウント設定 | 読み取り | 【必須】 | デプロイツールがアカウントIDを照合し、正しい場所へデプロイするために必要。 |
| ユーザーの詳細 | 読み取り | 【必須】 | トークン所有者の認証情報を確認するために使用されます。 |
| Workers Tail | 読み取り | 推奨 |
wrangler tail コマンドでリアルタイムログを確認(デバッグ)する際に使用します。 |
| Workers R2 Storage | 編集 | 任意 | R2(画像保存などのストレージ)を使用する場合に必要。 |
| Cloudflare Pages | 編集 | 任意 | 将来的に「Pages」としてデプロイする場合に使用。Workers構成なら基本不要。 |
| Workers の可観測性 | 編集 | 推奨 | APIのエラー率やアクセス統計などの分析データを記録・閲覧するために使用します。 |
|
その他 (Memberships, Buildsなど) |
- | 任意 | チーム所属確認や新しいビルド機能用。テンプレートに含まれていますが、削除しても基本動作には影響しません。 |
今回は、そのままテンプレートの権限を使用しますが、必須と推奨以外は必要に応じて削除しても問題ありません。また、そのプロジェクト応じて「アカウント,ユーザー,ゾーン」は適切に設定してください。
次にセキュリティの設定になります↓
- アカウントリソース:自身のアカウントを選択
- ゾーンリソース:カスタムドメインがある場合は、特定ゾーンを選択します。今回はないので一旦すべてのゾーンにします
- クライアントIPアドレスフィルタリング:設定なし。GITHUB ACTIONSのサーバー(Runner)は、毎回違うIPアドレスで動くため
- TTL:設定なし。起動する期間が決まっている場合は設定
設定が終わったら「概要に進む」→「APIトークン作成」を押下するとトークンが作成されます。後ほどGITHUB ACTIONSで使用するのでコピーします↓
2.GITHUBに鍵(APIトークン)を登録する。
pushしたリポジトリ選択「Settings」→「Secrets and variables」 → 「Actions」→「New repository secret」押下します↓
- CLOUDFLARE_API_TOKEN → さっきコピーしたトークン
- CLOUDFLARE_ACCOUNT_ID → アカウントID(CloudflareダッシュボードのURLの dash.cloudflare.com/ の後ろにある英数字)
の2点を登録し、次はローカルでymlを作成していきます。
3.ymlを作成し再push
ローカルのプロジェクト内に「.github/workflows/deploy.yml」を作成し、再pushします↓
name: Deploy to Cloudflare Workers
on:
push:
branches:
- main # mainブランチにプッシュされたら実行
jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Deploy with Wrangler
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# NotionAPIなどのシークレットは、
# Cloudflare側で保存(設定)するため、ここでは設定する必要はありません。
GITHUB ACTIONS画面で、ログを確認します(成功したか確認)↓
4.Notionの本番用環境変数を登録していきます。
コマンドで登録してもよいのですが、今回はcloudflare画面「コンピューティングとAI」→「Workers & Pages」→「notion-api」→「設定」→変数とシークレット「追加」から登録していきます。すべてシークレットで登録します↓
5.NotionAPI実行して確認
curl -X GET -H "x-service-secret: test" "https://発行されたURL/api"
終わりに
今回のソースコードは以下に公開しております↓
https://github.com/sato-1234/notion-api



















