30 分钟基于 Cloudflare Workers 构建 Nextjs 全栈开发项目,全程流水线介绍

基于 Cloudflare Workers 构建 Nextjs 全栈,与在 Vercel 上开发部署相比,大家都说有很多难点,Cloudflare 的特点是性价比高、赛博菩萨的美誉度。选择技术栈时,在 linux.do 社区看到某个帖子,参考了他的技术选型[1],对小白程度的新手来说,文章像明灯一样指引着我,特别有帮助。

December 15, 2025

开发方案

基于 Cloudflare Workers 构建 Nextjs 全栈,与在 Vercel 上开发部署相比,大家都说有很多难点。对我来说,我还没试过 Vercel,既然都有难点,那就直接选 Cloudflare Workers 好了。Cloudflare 的特点是性价比高、赛博菩萨的美誉度。如果我们搞通了 Workers,可以说我们上站噼里啪啦的,都不需要什么成本,在没有赚钱之前,顶多就是每月 5美元。

在正式开始之前,我尝试了很多方案,从简单的 html 纯静态,到 pages 动态方法,再到 workers 全栈的能力,大致了解了不同的产品如何针对不同的项目。选择技术栈时,在 linux.do 社区看到某个帖子,参考了他的技术选型[1],对小白程度的新手来说,这就像明灯一样指引着我,特别有帮助。

运行环境

  • cloudflare Workers 环境
  • cloudflare D1 数据库
  • cloudflare R2 对象存储
  • cloudflare Workers KV
  • cloudflare Workers AI
  • cloudflare Images

技术栈

  • React 库
  • Next.js 框架
  • Cloudflare OpenNext 适配器
  • TypeScript 类型语言
  • Drizzle ORM 对象关系映射
  • Better Auth 身份验证
  • Tailwindcss 样式库
  • Shadcn/UI 组件库

部署限制

如果账户是免费模式,目前的限制主要有以下 3 点:

特征 免费账户 付费账户
CPU 时间 每次10ms 每次 30ms
压缩包大小 3MB 10MB
项目数量 100个 500个

经过测试发现,压缩包大小限制 3MB,项目一般不太容易超过,如果采用多个Workers 组合形式也可以破解这一限制;关键是在CPU 每次10ms 比较容易触及,如果缓存策略到位的话还好(这是重点),否则会经常显示 503 错误。正式运营的项目,建议还是将账户升级为付费模式,缓存策略后续会具体说。

本地搭建

开发环境

本地开发环境包括采用 Linux 环境,通过VS Code 编辑器连接项目进行开发,具体配置如下:

  • Windows 11 WSL Ubuntu 系统 [2]
  • Windows 11 Visual Studio Code 编辑器与 WSL 插件
  • Windows 11 GitHub Desktop 可视化git 工具

初始化安装

下面以项目名称 blueseo-app 安装到 /home/cf/blueseo-app 为例,打开 WSL 终端依次输入命令如下:

// 文件夹授权(可选)
sudo chmod -R 777 /home

// 进入目录 /home/cf
cd /home/cf

// 创建 Nextjs 项目
npm create cloudflare@latest -- blueseo-app --framework=next

// 备用命令

// 修复依赖
npm install

// 安全漏洞
npm audit fix --force

// 删除文件夹
sudo rm -rf  /home/cf

资源创建与绑定

数据库名以 blueseo-db 为例,注意:存储桶 KV 都是我自己的命名,请修改为自己的。

// 进入新项目
cd blueseo-app

// 创建D1数据库
npx wrangler@latest d1 create blueseo-db

// 创建存储桶
npx wrangler r2 bucket create blueseo-bucket

// 创建 KV 命名空间
npx wrangler kv namespace create blueseo-kv

项目配置

Cloudflare Workers 有给 AI 大模型的提示词,针对 VS Code 编辑器,存放目录为 .github。

  • 增加 copilot-instructions.md,从 Workers 文档下载 [3]
  • 增加 requirement.md 你的项目需求书(备用)

Drizzle ORM 配置

  1. 安装 Drizzle ORM [4]

    // 安装 Drizzle ORM 包
    npm i drizzle-orm  dotenv
    npm i -D drizzle-kit tsx
    
    // 安装 drizzle-zod 依赖(支持从 Drizzle ORM 模式生成 Zod 模式)
    npm install drizzle-zod
    
  2. 创建声明表文件(以下为示例)

    // src/db/schema.ts
    // Drizzle ORM 数据库表声明
    
    // 选项表(例句)
    export const options = sqliteTable("options", {
      id: integer("id", { mode: 'number' }).primaryKey({ autoIncrement: true }),
      name: text("name").unique().notNull(),
      value: text("value"),
      autoload: integer("autoload", { mode: 'boolean' }).default(false),
      created_at: integer("created_at", { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),
      updated_at integer("updated_at", { mode: 'timestamp' }).notNull().default(sql`(unixepoch())`),
    });
    
  3. 创建配置文件(按照官方文档)

    // drizzle.config.ts
    // Drizzle Kit 配置文件
    
    import 'dotenv/config';
    import { defineConfig } from 'drizzle-kit';
    
    export default defineConfig({
      schema: './src/db/schema.ts',
      out: './drizzle',
      dialect: 'sqlite',
      driver: 'd1-http',
      dbCredentials: {
        accountId: process.env.CLOUDFLARE_ACCOUNT_ID as string,
        databaseId: process.env.CLOUDFLARE_DATABASE_ID as string,
        token: process.env.CLOUDFLARE_D1_TOKEN as string,
      },
    });
    

    增加环境变量,用于数据部署与迁移:

    // 本地环境变量.env
    CLOUDFLARE_ACCOUNT_ID= <你的 Cloudflare 账户 ID>
    CLOUDFLARE_DATABASE_ID= <当前绑定 d1_databases>
    CLOUDFLARE_D1_TOKEN= <你的含 D1 编辑权限的API 令牌>
    
  4. 增加 d1_databases 迁移模式

    // wrangler.jsonc
    {
        "d1_databases": [{
        "migrations_dir": "drizzle"
        }]
    }
    
  5. 增加 cloudflare 全局类型

    或运行 npm run cf-typegen 自动增加

    // cloudflare-env.d.ts
    CloudflareEnv 
    {
        DB: D1Database;
    }
    
  6. 迁移数据表命令

    Drizzle 生成迁移后,会创建与配置文件 drizzle.config.ts/out 目录的迁移文件

    // Drizzle 生成迁移脚本
    npx drizzle-kit generate
    
    // Drizzle 远程应用(可选)
     npx drizzle-kit migrate
    
    // Wrangler 应用迁移脚本(以数据库名 blueseo-db 为例)
    
    // 本地部署
    npx wrangler d1 migrations apply blueseo-db --local
    
    // 远程部署
    npx wrangler d1 migrations apply blueseo-db --remote
    
    // Wrangler 查询表格(备用)
    
    // 本地查询
    npx wrangler d1 execute blueseo-db --local --command="SELECT name FROM sqlite_master WHERE type='table'"
    
    // 远程查询
    npx wrangler d1 execute blueseo-db --remote --command="SELECT name FROM sqlite_master WHERE type='table'"
    
  7. 迁移触发器

    Drizzle 无法迁移触发器,需要 Wrangler 手动迁移。触发器我们一般存放到 sql/triggers.sql 位置。

    // 本地部署
    npx wrangler d1 execute blueseo-db --local --file=./sql/triggers.sql
    
    // 远程部署
    npx wrangler d1 execute blueseo-db --remote --file=./sql/triggers.sql
    
    // Wrangler 查询触发器(备用)
    
    // 本地查询
    npx wrangler d1 execute blueseo-db --local --command="SELECT name, sql FROM sqlite_master WHERE type = 'trigger'"
    
    // 远程查询
    npx wrangler d1 execute blueseo-db --remote --command="SELECT name, sql FROM sqlite_master WHERE type = 'trigger'"
    
  8. 增加脚本命令(可选)

    // package.json
    {
        "db:generate": "npx drizzle-kit generate",
        "db:local": "npx wrangler d1 migrations apply blueseo-db --local",
        "db:remote": "npx wrangler d1 migrations apply blueseo-db --remote",
        "db:local:trigger": "npx wrangler d1 execute blueseo-db --local --file=./sql/triggers.sql",
        "db:remote:trigger": "npx wrangler d1 execute blueseo-db --remote --file=./sql/triggers.sql",
    }
    
  9. 创建 Drizzle 客户端 [5]

    // src/db/index.ts
    
    // 导入 OpenNext 提供的上下文获取工具
    import { getCloudflareContext } from "@opennextjs/cloudflare";
    
    // 导入 drizzle 适配器
    import { drizzle, AnyD1Database } from "drizzle-orm/d1";
    
    // 导入数据库声明
    import * as schema from "@/db/schema";
    
    // 导入缓存模块
    import { cache } from "react";
    
    // Drizzle ORM 客户端实例化
    export const getDb = cache(async () => {
      try {
        // 运行时环境:返回 D1 数据库
        const { env } = await getCloudflareContext({ async: true });
        if (!env.DB) {
          throw new Error("D1 database not found");
        }
        return drizzle(env.DB, { schema });
      } catch {
        // 非运行环境(如本地 CLI/生成脚本)返回一个基于 null 的 drizzle 实例
        return drizzle(null as unknown as AnyD1Database, { schema });
      }
    });
    

    页面用例:

    // src/app/page.tsx
    
    // 导入 Drizzle 客户端
    import { db } from "@/db";
    
    // 导入数据库设计表
    import { user } from "@/db/schema";
    
    export default async function Home() {
    
      // 请求结果
      const results = await db.select().from(user).all();
      console.log(results);
    }
    

Better Auth 安装与配置

  1. 安装 Better Auth 包 [6]

    // 安装 Better Auth 包
    npm install better-auth
    
    // 生成安全密钥
    openssl rand -base64 32
    
  2. 设置环境变量

    • 谷歌社交登录 [7]
    • Github 社交登录 [8]
    // Better Auth
    BETTER_AUTH_SECRET= <自己生成的安全密钥>
    BETTER_AUTH_URL= <http://localhost:3000>
    
    // Google Auth
    GOOGLE_CLIENT_ID= <在谷歌获取的 ID>
    GOOGLE_CLIENT_SECRET= <在谷歌获取的 SECRET>
    
    // Github OAuth
    GITHUB_CLIENT_ID= <在 Github 获取的 ID>
    GITHUB_CLIENT_SECRET= <在 Github 获取的 SECRET>
    

    为 Cloudflare Workers 远程设置机密变量,本地和远程变量可以不一样。或非机密变量在 wrangler.jsonc/vars 中设置,机密变量必须通过 wrangler 设置或仪表盘设置。

    // 设置机密变量
    npx wrangler secret put GOOGLE_CLIENT_ID
    npx wrangler secret put GOOGLE_CLIENT_SECRET
    npx wrangler secret put GITHUB_CLIENT_ID
    npx wrangler secret put GITHUB_CLIENT_SECRET
    
    // 按提示输入变量即可
    
  3. 增加 cloudflare 全局类型

    或运行 npm run cf-typegen 自动增加

    // cloudflare-env.d.ts
    CloudflareEnv 
    {
        BETTER_AUTH_SECRET: string;
        BETTER_AUTH_URL: string;
        GOOGLE_CLIENT_ID: string;
        GOOGLE_CLIENT_SECRET: string;
    }
    
  4. 创建身份验证实例

    // src/lib/auth.ts
    // Better Auth 实例
    
    import { betterAuth } from "better-auth";
    import { nextCookies } from "better-auth/next-js";
    import { drizzleAdapter } from "better-auth/adapters/drizzle";
    import { getDb } from "@/db";
    
    // 创建异步函数来初始化 auth
    const createAuth = async () => {
      const db = await getDb();
      return betterAuth({
        // 使用 Drizzle ORM 配置数据库
        database: drizzleAdapter(db, {
          provider: "sqlite",
        }),
        // 环境变量密钥
        secret: process.env.BETTER_AUTH_SECRET as string,
        // 电子邮件和密码登陆
        emailAndPassword: {
          enabled: process.env.NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD === "true",
        },
        // 社交登陆
        socialProviders: {
          google: {
        clientId: process.env.GOOGLE_CLIENT_ID as string,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          },
          github: {
        clientId: process.env.GITHUB_CLIENT_ID as string,
        clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
          },
        },
        // 高级功能
        advanced: {
          useSecureCookies: true,
          database: {
        // 开启自增数字 ID(可选)
        useNumberId: true,
          },
        },
        // 插件
        plugins: [
          nextCookies(),
        ],
        // 用户表新增字段(可选)
        user: {
          additionalFields: {
        role: {
          type: "string",
          required: false,
          defaultValue: "user",
          input: false,
        },
          },
        },
        // Cookie 缓存(可选)
        session: {
          cookieCache: {
        enabled: true,
        // 缓存持续时间(秒)
        maxAge: 5 * 60,
          },
        },
      });
    }
    
    // 导出实例
    export const auth = await createAuth();
    
  5. 生成 ORM 模式或 SQL 迁移文件

    npx @better-auth/cli@latest generate --output=./src/db/schema/auth-schema.ts
    

    生成成功后使用 Drizzle 生成迁移并部署(Drizzle 第 6 步)

    // Drizzle 生成迁移脚本
    npx drizzle-kit generate
    
    // 本地部署
    npx wrangler d1 migrations apply blueseo-db --local
    
    // 远程部署
    npx wrangler d1 migrations apply blueseo-db --remote
    
  6. 挂载 API 处理程序

    // src/app/api/auth/[...all]/route.ts
    // Better Auth 登录 API
    
    import { auth } from "@/lib/auth";
    import { toNextJsHandler } from "better-auth/next-js";
    export const { POST, GET } = toNextJsHandler(auth);
    
  7. 创建客户端实例

    // src/lib/auth-client.ts
    // Better Auth 客户端实例
    
    // 导出客户端
    import { createAuthClient } from "better-auth/react";
    export const authClient = createAuthClient();
    
    export const {
      signIn,
      signOut,
      signUp,
      useSession
    } = authClient;
    
  8. 创建服务器端守卫(可选)

    // src/lib/auth-guard.ts
    // Better Auth 服务器端守卫
    
    import { auth } from "@/lib/auth";
    import { jsonResponse } from "@/utils/json-response";
    
    // 认证并获取用户会话
    export async function requireUser(
      request: Request,
    ) {
      const session = await auth.api.getSession({
        headers: request.headers,
      });
      if (!session?.user) {
        return jsonResponse({
          message: "Unauthorized.",
        }, {
          status: 401,
        });
      }
      return session;
    }
    

Cloudflare Images 图像优化

由 Cloudflare Images 提供的图像剪切服务,对标 nextjs 的 Image 图片优化。

  1. 新增自定义加载器

    R2 域名及网站域名下的图片,均使用该服务。外网图片 URL 不应该使用该服务,会导致错误。

    // src/image-loader.ts
    // 图片加载器
    import type { ImageLoaderProps } from "next/image";
    
    const normalizeSrc = (src: string) => {
        return src.startsWith("/") ? src.slice(1) : src;
    };
    
    export default function cloudflareLoader({ src, width, quality }: ImageLoaderProps) {
        if (process.env.NODE_ENV === "development") {
        return src;
        }
    
        // NEXT_PUBLIC_R2_PUBLIC_URL为 R2 绑定的独立图片域名(可选)
        const url = process.env.NEXT_PUBLIC_R2_PUBLIC_URL;
        if (src.startsWith('http') && (!url || !src.startsWith(url))) {
        return src;
        }
        const params = [`width=${width}`];
        if (quality) {
        params.push(`quality=${quality}`);
        }
        const paramsString = params.join(",");
        return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`;
    }
    
  2. next 配置更新

    将上述自定义加载器加入到配置环境中。

    // next.config.ts
    import type { NextConfig } from "next";
    const nextConfig: NextConfig = {
      // ...
      images: {
        loader: "custom",
        loaderFile: "./src/image-loader.ts",
      },
    };
    export default nextConfig;
    

增加对象缓存

这部分参考 Cloudflare OpenNext 适配器,OpenNext 官方写的比较仔细显得复杂,实际上我自己进一步进行了消化,如下标准化配置可满足绝大部分需求。

  1. 添加 Durable Object 绑定

    // wrangler.jsonc
    
    // 持久对象队列缓存
    "durable_objects": {
      "bindings": [
        {
          "name": "NEXT_CACHE_DO_QUEUE",
          "class_name": "DOQueueHandler"
        }
      ]
    },
    "migrations": [
      {
        "tag": "v1",
        "new_sqlite_classes": ["DOQueueHandler"]
      }
    ]
    
  2. 添加 queue 缓存配置

    // open-next.config.ts
    
    import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
    export default defineCloudflareConfig({
      // 其他配置
      queue: doQueue,
    });
    
  3. 创建 R2 增量缓存 + tagCache D1 资源

    // 使用 R2 存储增量缓存  
    npx wrangler r2 bucket create next-inc-cache
    
    // 使用 D1 标签缓存 (Next mode) 
    npx wrangler d1 create next-tag-cache
    

    检查或手动增加 wrangler.jsonc 的配置文件,应包含如下:

    • R2 增量缓存必须使用 binding = NEXT_INC_CACHE_R2_BUCKET
    • D1 标签缓存必须使用 binding = NEXT_TAG_CACHE_D1
    // wrangler.jsonc
    
    // 1. D1 标签缓存
    "d1_databases": [
      // 其他配置
      {
        "binding": "NEXT_TAG_CACHE_D1",
        "database_name": "next-tag-cache",
        "database_id": "xxxxxx"
      },
    ],
    
    // 2. R2 增量缓存
    "r2_buckets": [
      // 其他配置
      {
      "bucket_name": "next-inc-cache",
      "binding": "NEXT_INC_CACHE_R2_BUCKET"
      },
    ],
    
  4. 添加 R2 + D1 缓存配置

    // open-next.config.ts
    // 增加 tagCache 标识
    
     import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";
     import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
    // 将 Next 的标签缓存存储到 D1
    export default defineCloudflareConfig({
       tagCache: d1NextTagCache,
       incrementalCache: r2IncrementalCache,
    })
    
  5. 完整的 open-next.config 配置

    // open-next.config.ts
    // OpenNext 缓存策略
    
    import { defineCloudflareConfig } from "@opennextjs/cloudflare";
    import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache";
    import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache";
    import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue";
    
    export default defineCloudflareConfig({
      incrementalCache: r2IncrementalCache,
      queue: doQueue,
      tagCache: d1NextTagCache,
      enableCacheInterception: true,
    });
    

检查全局配置文件

  1. 手动检查 wrangler 配置

    // wrangler.jsonc
    
    // 兼容日期及标志(尽可能为最新)
    "compatibility_date": "2025-03-01",
    "compatibility_flags": [
        "nodejs_compat",
        "global_fetch_strictly_public"
    ],
    // 智能存放
    "placement": {
        "mode": "smart"
    },
    // 自身服务实例
    "services": [
      {
        "binding": "WORKER_SELF_REFERENCE",
        // 该服务应与当前项目名称相匹配
        "service": "blueseo-app"
      }
    ],
    // 同步的生成环境变量
    "vars": {
          // 社交渠道
      "GOOGLE_CLIENT_ID": "xxx",
      "GITHUB_CLIENT_ID": "xxx",
      // CF turnstile
      "TURNSTILE_SECRET_KEY": "xxx",
      // 构建变量
      "NEXT_PUBLIC_TURNSTILE_SITE_KEY": "xxx",
      "NEXT_PUBLIC_R2_PUBLIC_URL": "xxx",
    },
    

    关于 compatibility_flags 说明:

    • nodejs_compat 启用Node.js API [9]
    • global_fetch_strictly_public 允许在您的应用中获取URL [10]

    关于变量的说明:

    • 开发环境全部在 .env 文件中配置;
    • 生产环境非机密在 wrangler.jsonc/vars 中配置;
    • 生产环境机密在仪表盘或npx wrangler secret put {NAME}中配置;
    • 构建变量中加入所有以 NEXT_PUBLIC_ 开头的变量;
    • 更多环境变量规则见环境变量官方文档 [11]
  2. 手动检查 tsconfig 配置

    // tsconfig.json
    
    // ECMAScript 输出版本‌
    "compilerOptions": {
      "target": "ES2020",
     }
    
  3. 手动检查 cloudflare-env 类型配置

    // cloudflare-env.d.ts
    
    declare namespace Cloudflare {
        interface Env {
        // 绑定资源类型
        DB: D1Database;
        KV: KVNamespace;
        UPLOADS: R2Bucket;
        NEXT_PUBLIC_R2_PUBLIC_URL: string;
    
        // 旋转门类型(可选)
        TURNSTILE_SECRET_KEY: string;
        NEXT_PUBLIC_TURNSTILE_SITE_KEY: string;
        
        // 社交登录类型(可选)
        GOOGLE_CLIENT_ID: string;
        GITHUB_CLIENT_ID: string;
    
        // 缓存相关类型
        NEXT_CACHE_DO_QUEUE: DurableObjectNamespace<import("./.open-next/worker").DOQueueHandler>;
        NEXT_INC_CACHE_R2_BUCKET: R2Bucket;
        NEXT_TAG_CACHE_D1: D1Database;
        
        // 其他类型
        WORKER_SELF_REFERENCE: Service<typeof import("./.open-next/worker").default>;
        NEXTJS_ENV: string;
        ASSETS: Fetcher;
        }
    }
    
  4. 手动检查其他配置

    • .dev.vars 除了 NEXTJS_ENV=development 外,不应该包括任何其他。
    • .env 包含 NODE_ENV="development",及所有本地环境变量。
    • .gitignore 中不应该排除 .github、drizzle、sql等目录。

UI 组件安装与配置

  1. 基础组件

    // 增加黑暗模式
    npm install next-themes
    
    // 增加顶部加载条(可选)
    npm install nextjs-toploader
    
  2. 安装 Shadcn/UI 组件库

    // 安装 Shadcn 包
    npx shadcn@latest init
    
    // 增加需要的组件
    npx shadcn@latest add button button-group label checkbox input textarea select switch form dropdown-menu navigation-menu menubar tabs breadcrumb pagination card table avatar separator skeleton sonner carousel resizable toggle tooltip drawer dialog alert empty sidebar spinner item badge alert-dialog collapsible aspect-ratio popover command input-group field toggle-group
    
  3. 安装 markdown 组件

    // 渲染插件
    npm install react-markdown remark-gfm rehype-sanitize rehype-highlight rehype-raw highlight.js
    
  4. 安装 @tailwindcss/typography

    // @tailwindcss/typography 插件
    npm install -D @tailwindcss/typography
    
    /* src/app/globals.css 新增 */
    @plugin "@tailwindcss/typography";
    

    VS code 编辑器插件安装:

    • Tailwind CSS IntelliSense
    • PostCSS Language Support
  5. 安装时间格式

    npm install dayjs
    npm install utc timezone
    

以上就是基于 Cloudflare Workers 构建 Nextjs 全栈开发项目安装与配置顺序,当我开发新的项目时,当我需要从头开始,必须以此为指南,一般情况下 30 分钟完成。

参考资料

[1]: 独立开发技术栈 2025

[2]: Windows 11 WSL Ubuntu 安装步骤

[3]: Workers Prompt 文档下载

[4]: Drizzle D1 官方文档

[5]: Opennext Drizzle 官方文档

[6]: Better-auth 官方文档

[7]: Google Cloud Console 社交登录申请

[8]: Developer applications 社交登录申请

[9]: 启用Node.js API

[10]: 允许在您的应用中获取URL

[11]: 环境变量官方文档