cloudflare-opennext について
Cloudflare OpenNext は、Next.js アプリを Cloudflare Workers にデプロイするためのアダプター兼スキルです。新規デプロイや移行を行う開発者向けで、App Router、Pages Router、ISR、SSG、Cloudflare バインディングに対応し、ビルドとプレビューのコマンドを備えています。
主な機能
- 01Next.js のビルド出力を Workers 向けに変換
- 02App Router と Pages Router に対応
- 03SSG・SSR・ISR に対応
- 04Cloudflare バインディングへのアクセス
- 05ビルド・プレビュー・デプロイ用コマンド
活用シーン
- 01新しい Next.js アプリの Workers へのデプロイ
- 02既存 Next.js アプリの Cloudflare への移行
- 03ISR または SSG のキャッシュ設定
- 04ルートからの Cloudflare バインディング利用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ null-shot/ cloudflare-skills --skill cloudflare-opennextインストール後、Claude Code で/cloudflare-opennextを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/caching.mdconfiguration.mddatabase-orm.mdtroubleshooting.md詳細
Cloudflare OpenNext
@opennextjs/cloudflare アダプターを使用して、App Router、Pages Router、ISR、SSG、Cloudflare バインディングを完全にサポートする Next.js アプリケーションを Cloudflare Workers にデプロイします。
使用する場面
- Cloudflare Workers 向けに新しい Next.js アプリを作成する場合
- 既存の Next.js アプリを Cloudflare に移行する場合
- R2、KV、D1 を使用した ISR/SSG キャッシュを設定する場合
- Cloudflare バインディング(KV、R2、D1、Durable Objects、AI)にアクセスする場合
- Next.js でデータベースや ORM(Drizzle、Prisma)を使用する場合
- デプロイの問題やバンドルサイズの問題を解決する場合
はじめに
新しいアプリ
npm create cloudflare@latest -- my-next-app --framework=next --platform=workers
cd my-next-app
npm run dev # Local development with Next.js
npm run preview # Preview in Workers runtime
npm run deploy # Deploy to Cloudflare
既存アプリの移行
# 1. Install dependencies
npm install @opennextjs/cloudflare@latest
npm install --save-dev wrangler@latest
# 2. Create wrangler.jsonc (see Configuration section)
# 3. Create open-next.config.ts
# 4. Update next.config.ts
# 5. Add scripts to package.json
# 6. Deploy
npm run deploy
コアコンセプト
OpenNext の仕組み
@opennextjs/cloudflare アダプターは次の処理を行います。
next buildを実行して Next.js のビルド出力を生成します- ビルド出力を Cloudflare Workers ランタイムで動作するように変換します
worker.jsエントリーポイントを含む.open-next/ディレクトリに出力します- 静的ファイル(
_next/static、public)には Workers Static Assets を使用します
Node.js ランタイム(Edge ではありません)
重要:OpenNext は Next.js の Node.js ランタイムを使用し、Edge ランタイムは使用しません。
// ❌ Remove this - Edge runtime not supported
export const runtime = "edge";
// ✅ Default Node.js runtime - fully supported
// No export needed, this is the default
Node.js ランタイムでは、次のことが可能です。
nodejs_compatフラグによる Node.js API との完全な互換性- Edge ランタイムより多くの Next.js 機能の利用
- すべての Cloudflare バインディングへのアクセス
設定ファイル
wrangler.jsonc
OpenNext の最小構成:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "my-nextjs-app",
"main": ".open-next/worker.js",
"compatibility_date": "2024-12-30",
"compatibility_flags": [
"nodejs_compat", // Required for Node.js APIs
"global_fetch_strictly_public" // Security: prevent local IP fetches
],
"assets": {
"directory": ".open-next/assets", // Static files
"binding": "ASSETS"
},
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
"service": "my-nextjs-app" // Must match "name" above
}
],
"images": {
"binding": "IMAGES" // Optional: Enable image optimization
}
}
必須の設定:
nodejs_compat互換性フラグcompatibility_date>=2024-09-23WORKER_SELF_REFERENCEサービスバインディング(ワーカー名と一致させる必要があります)mainとassetsのパスは変更しないでください
R2、KV、D1 バインディングを含む完全な設定については、references/configuration.md を参照してください。
open-next.config.ts
キャッシュと OpenNext の動作を設定します。
import { defineCloudflareConfig } from "@opennextjs/cloudflare";
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
export default defineCloudflareConfig({
incrementalCache: r2IncrementalCache,
});
このファイルが存在しない場合は、自動生成されます。キャッシュオプションについては、references/caching.md を参照してください。
next.config.ts
ローカル開発用に OpenNext を初期化します。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Your Next.js configuration
};
export default nextConfig;
// Enable bindings access during `next dev`
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
initOpenNextCloudflareForDev();
.dev.vars
ローカル開発用の環境変数:
# .dev.vars
NEXTJS_ENV=development
NEXTJS_ENV 変数は、読み込む Next.js の .env ファイルを選択します。
development→.env.developmentproduction→.env.production(デフォルト)
Cloudflare バインディングへのアクセス
getCloudflareContext() を使用すると、任意のルートでバインディングにアクセスできます。
import { getCloudflareContext } from "@opennextjs/cloudflare";
// Route Handler (App Router)
export async function GET(request: Request) {
const { env, cf, ctx } = getCloudflareContext();
// Access KV
const value = await env.MY_KV.get("key");
// Access R2
const object = await env.MY_BUCKET.get("file.txt");
// Access D1
const result = await env.DB.prepare("SELECT * FROM users").all();
// Access Durable Objects
const stub = env.MY_DO.idFromName("instance-1");
const doResponse = await stub.fetch(request);
// Access request info
const country = cf?.country;
// Background tasks
ctx.waitUntil(logAnalytics());
return Response.json({ value });
}
// API Route (Pages Router)
export default async function handler(req, res) {
const { env } = getCloudflareContext();
const data = await env.MY_KV.get("key");
res.json({ data });
}
// Server Component
export default async function Page() {
const { env } = getCloudflareContext();
const data = await env.MY_KV.get("key");
return <div>{data}</div>;
}
非同期コンテキストを使用する SSG ルート
静的サイト生成ルートでは、非同期モードを使用します。
// In SSG route (generateStaticParams, etc.)
const { env } = await getCloudflareContext({ async: true });
const products = await env.DB.prepare("SELECT * FROM products").all();
警告:SSG の実行中は、.dev.vars のシークレットとローカルバインディングの値が静的ビルドに含まれます。機密データの取り扱いには注意してください。
TypeScript 型
バインディングの型を生成します。
npx wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts
package.json に追加します。
{
"scripts": {
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
}
}
wrangler.jsonc のバインディングを変更した後は、毎回実行してください。
CLI コマンド
opennextjs-cloudflare CLI は、OpenNext 固有の動作を追加した Wrangler のラッパーです。
# Build the Next.js app and transform for Workers
npx opennextjs-cloudflare build
# Build and preview locally with Wrangler
npm run preview
# or
npx opennextjs-cloudflare preview
# Build and deploy to Cloudflare
npm run deploy
# or
npx opennextjs-cloudflare deploy
# Build and upload as a version (doesn't deploy)
npm run upload
# or
npx opennextjs-cloudflare upload
# Populate cache (called automatically by preview/deploy/upload)
npx opennextjs-cloudflare populateCache local # Local bindings
npx opennextjs-cloudflare populateCache remote # Remote bindings
推奨する package.json スクリプト:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy",
"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload",
"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts"
}
}
キャッシュ戦略
OpenNext は、Cloudflare ストレージを使用した Next.js のキャッシュに対応しています。
| キャッシュの種類 | 用途 | ストレージの選択肢 |
|---|---|---|
| インクリメンタルキャッシュ | ISR/SSG のページデータ | R2、KV、Static Assets |
| キュー | 時間ベースの再検証 | Durable Objects、Memory |
| タグキャッシュ | オンデマンドの再検証 | D1、Durable Objects |
簡単な設定例:
// Static Site (SSG only)
import staticAssetsCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache";
export default defineCloudflareConfig({
incrementalCache: staticAssetsCache,
enableCacheInterception: true,
});
// Small Site with ISR
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
export default defineCloudflareConfig({
incrementalCache: r2IncrementalCache,
queue: doQueue,
tagCache: d1NextTagCache,
});
リージョナルキャッシュやシャーディングされたタグキャッシュを含む完全なキャッシュパターンについては、references/caching.md を参照してください。
画像の最適化
Cloudflare Images を有効にすると、画像を自動で最適化できます。
// wrangler.jsonc
{
"images": {
"binding": "IMAGES"
}
}
Next.js の <Image> コンポーネントは、自動的に Cloudflare Images を使用します。追加料金が発生します。
互換性に関する注意事項:
- 対応形式:PNG、JPEG、WEBP、AVIF、GIF、SVG
minimumCacheTTLはサポートされていませんdangerouslyAllowLocalIPはサポートされていません
データベースと ORM のパターン
重要なルール:Workers でグローバルなデータベースクライアントを作成しないでください。リクエストごとに作成します。
// ❌ WRONG - Global client causes I/O errors
import { Pool } from "pg";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
// ✅ CORRECT - Per-request client
import { cache } from "react";
import { Pool } from "pg";
export const getDb = cache(() => {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
maxUses: 1, // Don't reuse connections across requests
});
return drizzle({ client: pool, schema });
});
// Usage in route
export async function GET() {
const db = getDb();
const users = await db.select().from(usersTable);
return Response.json(users);
}
Drizzle と Prisma のパターンについては、references/database-orm.md を参照してください。
重要なルール
✅ 使う
- Node.js ランタイムを使う - デフォルトのランタイムです。
export const runtime = "edge"は削除します。 - リクエストごとに DB クライアントを作成する - React の
cache()を使い、リクエストスコープのインスタンスを作成します。 - nodejs_compat を有効にする -
date >= 2024-09-23の互換性フラグが必要です。 - getCloudflareContext() を使う - バインディングへのアクセスには、next-on-pages の getRequestContext ではなくこちらを使います。
- .open-next を .gitignore に追加する - ビルド出力はコミットしないでください。
- wrangler.jsonc を使う - wrangler.toml ではありません(JSONC はコメントと検証に対応しています)。
- WORKER_SELF_REFERENCE を設定する - サービスバインディングはワーカー名と一致させる必要があります。
- public/_headers を追加する - 静的アセットのキャッシュヘッダーを設定します。
❌ 使わない
- Edge ランタイムを使わない - すべてのルートから
export const runtime = "edge"を削除します。 - Turbopack を使わない -
next build --turboではなく、next buildを使います。 - グローバル DB クライアントを作成しない - 「Cannot perform I/O」エラーの原因になります。
- 10 MiB を超えない - ワーカーのサイズ制限です(無料プランでは 3 MiB)。
- next-on-pages を使わない - 別のアダプターです。代わりに @opennextjs/cloudflare を使います。
- .open-next/ をコミットしない - ビルド出力ディレクトリです。
- Node Middleware を使わない - サポートされていません(Next.js 15.2 以降の機能)。
サポートされる機能
| 機能 | サポート | 備考 |
|---|---|---|
| App Router | ✅ 完全対応 | すべての機能に対応 |
| Pages Router | ✅ 完全対応 | API ルートを含む |
| Route Handlers | ✅ 完全対応 | GET、POST など |
| Dynamic Routes | ✅ 完全対応 | [slug]、[...slug] |
| SSG | ✅ 完全対応 | 静的サイト生成 |
| SSR | ✅ 完全対応 | サーバーサイドレンダリング |
| ISR | ✅ 完全対応 | インクリメンタル静的再生成 |
| PPR | ✅ 完全対応 | 部分的プリレンダリング |
| Middleware | ✅ 一部対応 | 標準のミドルウェアには対応していますが、Node Middleware(15.2 以降)には対応していません |
| Image Optimization | ✅ 完全対応 | Cloudflare Images バインディング経由 |
| Composable Caching | ✅ 完全対応 | 'use cache'ディレクティブ |
| next/font | ✅ 完全対応 | フォントの最適化 |
| after() | ✅ 完全対応 | バックグラウンドタスク |
| Turbopack | ❌ 非対応 | 標準のビルドを使ってください |
サポートされる Next.js のバージョン:
- Next.js 15:すべてのマイナーバージョンおよびパッチバージョン
- Next.js 14:最新のマイナーバージョンのみ
開発ワークフロー
# Local development with Next.js dev server
npm run dev
# Preview in Workers runtime (faster than deploy)
npm run preview
# Deploy to production
npm run deploy
# Update TypeScript types after binding changes
npm run cf-typegen
ローカル開発に関する注意:
next dev- Node.js ランタイムを使用します。バインディングはinitOpenNextCloudflareForDev()経由で利用できます。npm run preview- Wrangler を使用して Workers ランタイムを実行します。本番環境に近い動作です。- どちらもホットリロードに対応しています。
詳細なリファレンス
- references/configuration.md - wrangler.jsonc、環境変数、TypeScript 型の全設定
- references/caching.md - ISR、SSG、R2/KV/D1 キャッシュ、タグキャッシュ、キュー、キャッシュのパージ
- references/database-orm.md - D1、PostgreSQL、Hyperdrive を使用した Drizzle、Prisma のセットアップ
- references/troubleshooting.md - サイズ制限、バンドル分析、よくあるエラー
@cloudflare/next-on-pages からの移行
@cloudflare/next-on-pagesから移行する場合:
@cloudflare/next-on-pagesとeslint-plugin-next-on-pagesをアンインストールします。@opennextjs/cloudflareをインストールします。next.config.tsを更新します:setupDevPlatform()の呼び出しを削除します。initOpenNextCloudflareForDev()に置き換えます。
- インポートを更新します:
@cloudflare/next-on-pagesのgetRequestContextを置き換えます。@opennextjs/cloudflareのgetCloudflareContextを使います。
- Edge ランタイムのエクスポート(
export const runtime = "edge")を削除します。 - 必須の OpenNext 設定を使って wrangler.jsonc を更新します。
- next-on-pages の ESLint ルールを削除します。
例
公式の例は@opennextjs/cloudflare リポジトリにあります:
create-next-app- Next.js の基本スターターmiddleware- ミドルウェアの使用例vercel-blog-starter- SSG ブログの例
ベストプラクティス
- シンプルに始める - SSG のみのサイトには Static Assets キャッシュを使用します
- キャッシュを段階的に追加する - ISR が必要になったら R2 キャッシュを有効にします
- バンドルサイズを監視する - 圧縮後 10 MiB 未満に保ちます(ESBuild Bundle Analyzer を使用します)
- TypeScript を使用する -
cf-typegenを実行してバインディングの型を取得します - プレビューでテストする - デプロイ前に
npm run previewを使用します - データベースクライアントをキャッシュする - リクエストごとのインスタンスには React の
cache()を使用します - 可観測性を有効にする - ロギングのために wrangler.jsonc に
observabilityを追加します - ビルドにリモートバインディングを使用する - 実際のデータを使った ISR のために有効にします
一般的なパターン
references/configuration.md で、以下を含む完全な例をご覧ください:
- 複数のハンドラー(fetch、scheduled、queue)を持つカスタム Worker
- 環境ごとの設定(staging、production)
- ビルド時のデータアクセスに使用するリモートバインディング