Cloudflare Pages FunctionsでAPIルートを追加する実装手順【個人開発・2025年版】
結論:静的サイトの機能追加はPages Functionsのfunctions/以下にファイルを置くだけで実現できる
AstroやNext.jsの静的書き出しサイトに「フォーム送信」「外部APIの認証プロキシ」「簡易的な集計API」を足したいとき、わざわざ別途サーバーやCloudflare Workersプロジェクトを新設する必要はない。
Cloudflare PagesにはPages Functionsという仕組みがあり、リポジトリ内にfunctions/ディレクトリを作ってファイルを置くだけで、そのファイルパスがそのままAPIルートになる。追加のデプロイ設定もインフラ構築も不要で、静的サイトのビルド・デプロイフローに乗ったまま裏側にサーバーレス処理を足せる。
個人開発で「ちょっとしたAPIエンドポイントだけ欲しい」という場面には、これが最も手数が少ない選択肢だ。以下、具体的な手順を追って説明する。
Pages FunctionsとCloudflare Workersの違い
最初に混同しやすいポイントを整理する。
| 項目 | Pages Functions | Cloudflare Workers(単体) |
|---|---|---|
| デプロイ単位 | 静的サイトと同一プロジェクト | 独立したプロジェクト |
| ルーティング | ファイルパス=URLパス(規約ベース) | wrangler.tomlで手動設定 |
| 向いている規模 | 静的サイトに付随する小〜中規模API | API単体・複数サービス連携 |
| 実行環境 | Workers runtime(同一) | Workers runtime |
| 料金 | Workers Free/Paidプランに準拠 | 同左 |
実行環境自体はどちらもWorkers runtimeなので性能差はない。違いは「デプロイの単位とルーティングの決め方」だけだと理解しておくとよい。既存の静的サイトに軽く機能を足したいなら Pages Functions、APIを本格的に育てて複数サイトから使い回すならWorkers単体、という使い分けになる。
料金体系は変更されることがあるため、契約前に必ず公式の最新情報を確認してほしい。
実装手順
1. functionsディレクトリを作る
プロジェクトルート直下(静的サイトのビルド出力とは別)にfunctions/を作成する。Astroであればsrc/とは別に、リポジトリのトップレベルに置く。
my-site/
├── src/
├── functions/
│ └── api/
│ └── contact.ts
└── wrangler.toml
2. APIルートを実装する
functions/api/contact.tsに置いたファイルは、そのまま/api/contactというURLでアクセスできるようになる。
export const onRequestPost: PagesFunction = async (context) => {
const { request, env } = context;
const body = await request.json();
if (!body.email || !body.message) {
return new Response(JSON.stringify({ error: "invalid params" }), {
status: 400,
headers: { "Content-Type": "application/json" },
});
}
// 例: Slack Webhookへ通知を転送する
await fetch(env.SLACK_WEBHOOK_URL, {
method: "POST",
body: JSON.stringify({ text: `お問い合わせ: ${body.email} - ${body.message}` }),
});
return new Response(JSON.stringify({ ok: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
};
onRequestGet / onRequestPostのようにHTTPメソッドごとに関数をエクスポートするのが規約。onRequest(メソッド無指定)を使えば全メソッドをまとめて受けることもできる。
3. 動的ルートを扱う
ファイル名を[id].tsのように角括弧にすると動的パラメータとして扱える。
functions/api/users/[id].ts → /api/users/123 にマッチ
export const onRequestGet: PagesFunction = async (context) => {
const id = context.params.id;
return new Response(JSON.stringify({ userId: id }));
};
4. 環境変数・シークレットを設定する
Slack Webhook URLやAPIキーなどの秘匿情報は、コードに直書きせずCloudflareダッシュボードの「Settings > Environment variables」またはCLIから設定する。
wrangler pages secret put SLACK_WEBHOOK_URL --project-name my-site
ローカル開発では.dev.varsファイルに同名の変数を書いておけば、wrangler pages dev実行時に読み込まれる。このファイルは.gitignoreに必ず入れておくこと。
5. ミドルウェアで共通処理をまとめる
認証チェックやCORS設定など複数ルートで共通する処理は、functions/_middleware.tsに書くとディレクトリ配下すべてに適用される。
export const onRequest: PagesFunction = async (context) => {
const response = await context.next();
response.headers.set("Access-Control-Allow-Origin", "*");
return response;
};
6. ローカル検証してからデプロイする
npx wrangler pages dev ./dist
ビルド出力ディレクトリを指定して起動すると、静的ファイルとFunctionsの両方をローカルで動作確認できる。ここで一度動かしてからGitにpushしてデプロイする流れにすると、本番で初めてエラーに気づく事態を避けられる。
つまずきやすいポイントと注意点
- KVやD1へのバインディングは
wrangler.tomlに明示が必要。 バインディング名を打ち間違えるとenv.DBがundefinedになり原因が分かりにくい。DBを使う構成にするならCloudflare D1 × Drizzle ORM マイグレーション運用手順も合わせて確認しておくと設計がぶれない。 - Node.js組み込みAPIは基本的に使えない。
fsやcryptoのNode版に依存したライブラリはWorkers環境で動かないことが多く、Web標準API(fetch、crypto.subtleなど)で書き直す必要がある。 - 実行時間・メモリに制限がある。 重い画像処理やバッチ集計のような長時間処理には向かない。そうした処理は別途キューイングする構成のほうが安定する。
- ルーティング競合に注意。 静的ファイルと同じパスに
functions/のファイルを置くと、どちらが優先されるか分かりにくくなることがある。API用のパスは/api/配下に統一しておくと事故が減る。
これらはいずれも「サーバーレスだから性能が悪い」という話ではなく、実行環境の制約を把握しないまま設計すると詰まる、という種類の注意点だ。仕様は変更されることがあるため、着手前に公式ドキュメントの最新版を確認してほしい。
まとめ
Cloudflare Pages Functionsは、既存の静的サイトに手数少なくサーバーレスAPIを足せる選択肢だ。functions/以下にファイルを置くだけでルーティングが決まり、追加のデプロイ設定もほぼ不要になる。
個人開発でお問い合わせフォームや簡易APIが欲しいだけなら、まずこの構成で十分間に合う。デプロイ手順そのものに不安がある場合はCloudflare PagesにAstroサイトをデプロイする方法を先に確認しておくとスムーズに進められる。