mcpField
C
cloudflare-opennext

cloudflare-opennext

github.com/null-shot云与 DevOps0464 次安装41 周前更新
代码仓库

关于 cloudflare-opennext

Cloudflare OpenNext 是将 Next.js 应用部署到 Cloudflare Workers 的适配器与技能,适合新建部署或迁移应用的开发者。它支持 App Router、Pages Router、ISR、SSG 及 Cloudflare 绑定,并提供构建和预览命令。

主要功能

  1. 01将 Next.js 构建输出转换为 Workers
  2. 02支持 App Router 与 Pages Router
  3. 03支持 SSG、SSR 和 ISR
  4. 04接入 Cloudflare 绑定
  5. 05提供构建、预览和部署命令

适用场景

  1. 01将新建的 Next.js 应用部署到 Workers
  2. 02把现有 Next.js 应用迁移至 Cloudflare
  3. 03为 ISR 或 SSG 配置缓存
  4. 04在路由中使用 Cloudflare 绑定

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/null-shot/cloudflare-skills --skill cloudflare-opennext

安装后在 Claude Code 中输入/cloudflare-opennext确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
caching.md
configuration.md
database-orm.md
troubleshooting.md

详细介绍

Cloudflare OpenNext

使用 @opennextjs/cloudflare 适配器将 Next.js 应用部署到 Cloudflare Workers,全面支持 App Router、Pages Router、ISR、SSG 和 Cloudflare 绑定。

适用场景

  • 为 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 适配器:

  1. 运行 next build 以生成 Next.js 构建输出
  2. 转换构建输出,使其能够在 Cloudflare Workers 运行时中工作
  3. 将输出写入 .open-next/ 目录,入口文件为 worker.js
  4. 使用 Workers Static Assets 提供静态文件(_next/static、public)
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-23
  • WORKER_SELF_REFERENCE 服务绑定(必须与 Worker 名称一致)
  • 不应更改 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.development
  • production → .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 为 Wrangler 封装了 OpenNext 专属行为:

# 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、内存
标签缓存按需重新验证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。

重要规则

✅ 应该做
  1. 使用 Node.js 运行时 - 默认运行时,移除任何 export const runtime = "edge"
  2. 为每个请求创建数据库客户端 - 使用 React 的 cache() 创建请求范围的实例
  3. 启用 nodejs_compat - 必需的兼容性标志,且日期须为 2024-09-23 或之后
  4. 使用 getCloudflareContext() - 访问绑定,不要使用 next-on-pages 中的 getRequestContext
  5. 将 .open-next 添加到 .gitignore - 不应提交构建输出
  6. 使用 wrangler.jsonc - 不要使用 wrangler.toml(JSONC 支持注释和验证)
  7. 设置 WORKER_SELF_REFERENCE - 服务绑定必须与 Worker 名称一致
  8. 添加 public/_headers - 配置静态资源缓存标头
❌ 不要做
  1. 不要使用 Edge 运行时 - 从所有路由中移除 export const runtime = "edge"
  2. 不要使用 Turbopack - 使用 next build,不要使用 next build --turbo
  3. 不要创建全局数据库客户端 - 会导致 “Cannot perform I/O” 错误
  4. 不要超过 10 MiB - Worker 大小限制(免费套餐为 3 MiB)
  5. 不要使用 next-on-pages - 这是不同的适配器,请改用 @opennextjs/cloudflare
  6. 不要提交 .open-next/ - 这是构建输出目录
  7. 不要使用 Node 中间件 - 不受支持(Next.js 15.2+ 功能)

支持的功能

功能支持情况备注
App Router✅ 完全支持支持所有功能
Pages Router✅ 完全支持包括 API 路由
Route Handlers✅ 完全支持GET、POST 等
动态路由✅ 完全支持[slug]、[...slug]
SSG✅ 完全支持静态站点生成
SSR✅ 完全支持服务端渲染
ISR✅ 完全支持增量静态再生成
PPR✅ 完全支持部分预渲染
Middleware✅ 部分支持支持标准中间件,不支持 Node 中间件(15.2+)
图像优化✅ 完全支持通过 Cloudflare Images 绑定实现
可组合缓存✅ 完全支持'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 运行时,更接近生产环境
  • 两者都支持热重载

详细参考

从 @cloudflare/next-on-pages 迁移

如果要从 @cloudflare/next-on-pages 迁移:

  1. 卸载 @cloudflare/next-on-pages 和 eslint-plugin-next-on-pages
  2. 安装 @opennextjs/cloudflare
  3. 更新 next.config.ts:
    • 移除 setupDevPlatform() 调用
    • 替换为 initOpenNextCloudflareForDev()
  4. 更新导入:
    • 移除从 @cloudflare/next-on-pages 导入的 getRequestContext
    • 使用从 @opennextjs/cloudflare 导入的 getCloudflareContext
  5. 移除 Edge 运行时导出(export const runtime = "edge")
  6. 使用必需的 OpenNext 设置更新 wrangler.jsonc
  7. 移除 next-on-pages eslint 规则

示例

@opennextjs/cloudflare 仓库中的官方示例:

  • create-next-app - 基础 Next.js 入门项目
  • middleware - 中间件用法
  • vercel-blog-starter - SSG 博客示例

最佳实践

  1. 从简单开始 - 仅限 SSG 的网站使用 Static Assets 缓存
  2. 逐步添加缓存 - 需要 ISR 时启用 R2 缓存
  3. 监控打包体积 - 保持压缩后小于 10 MiB(使用 ESBuild Bundle Analyzer)
  4. 使用 TypeScript - 运行 cf-typegen 获取绑定类型
  5. 使用预览进行测试 - 部署前运行 npm run preview
  6. 缓存数据库客户端 - 使用 React 的 cache() 创建每个请求对应的实例
  7. 启用可观测性 - 在 wrangler.jsonc 中添加 observability 以记录日志
  8. 使用远程绑定进行构建 - 启用此功能,以便 ISR 使用真实数据

常见模式

请参阅 references/configuration.md,了解完整示例,包括:

  • 包含多个处理程序(fetch、scheduled、queue)的自定义 Worker
  • 特定环境的配置(staging、production)
  • 用于在构建时访问数据的远程绑定

资源

评论

支持 Markdown