后端与接口
在 Cloudflare Workers 上从零搭建…
当用户提到 TanStack Start、Cloudflare 全栈 SSR 应用、SSR dashboard 或 React 19 + Workers 文件路由/Server Functions 时,按此经验从零生成并部署完整应用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
在 Cloudflare Workers特定
解决思路
- 不克隆模板、不运行 scaffold 命令;直接逐文件生成整个项目,技术栈为 TanStack Start v1(SSR、文件路由、Nitro server functions)运行在 Cloudflare Workers,React 19 + Tailwind v4 + shadcn/ui,D1 + Drizzle,better-auth(Google OAuth + email/password)。
- 按文件树创建项目结构:src/routes/__root.tsx、index.tsx、login.tsx、register.tsx、_authed.tsx、_authed/dashboard.tsx、_authed/items.tsx、_authed/items.$id.tsx、_authed/items.new.tsx、api/auth/$.ts;src/components/ui、app-sidebar.tsx、theme-toggle.tsx、user-nav.tsx、stat-card.tsx;src/db/schema.ts、index.ts;src/lib/auth.server.ts、auth.client.ts、utils.ts;src/server/functions.ts;src/styles/app.css;src/router.tsx、client.tsx、ssr.tsx、routeTree.gen.ts;drizzle/、public/、vite.config.ts、wrangler.jsonc、drizzle.config.ts、tsconfig.json、package.json、.dev.vars、.gitignore。
- 不要编辑 src/routeTree.gen.ts,它是自动生成的路由树;在 src/lib/utils.ts 中实现 shadcn/ui 使用的 cn() helper;public/ 放静态资源(如 favicon)。
- 安装 runtime 依赖:react ^19.0.0、react-dom ^19.0.0、@tanstack/react-router ^1.120.0、@tanstack/react-start ^1.120.0、drizzle-orm ^0.38.0、better-auth ^1.2.0、zod ^3.24.0、class-variance-authority ^0.7.0、clsx ^2.1.0、tailwind-merge ^3.0.0、lucide-react ^0.480.0。
- 安装 dev 依赖:@cloudflare/vite-plugin ^1.0.0、@tailwindcss/vite ^4.0.0、@vitejs/plugin-react ^4.4.0、tailwindcss ^4.0.0、typescript ^5.7.0、drizzle-kit ^0.30.0、wrangler ^4.0.0、tw-animate-css ^1.2.0。
- 在 package.json scripts 设置:dev 为 vite、build 为 vite build、preview 为 vite preview、deploy 为 wrangler deploy、db:generate 为 drizzle-kit generate、db:migrate:local 为 wrangler d1 migrations apply PROJECT_NAME-db --local、db:migrate:remote 为 wrangler d1 migrations apply PROJECT_NAME-db --remote。
- 收集项目信息:必填项目名 kebab-case、一句话描述、Cloudflare 账号、认证方式(Google OAuth、email/password 或两者);可选 Google OAuth 凭据、自定义域名、是否需要 R2、管理员邮箱。
- 创建项目目录和所有配置文件,全部从零生成,不使用模板克隆或 scaffold。
- 配置 vite.config.ts:插件顺序必须为 cloudflare({ viteEnvironment: { name: "ssr" } }) -> tailwindcss() -> tanstackStart() -> viteReact()。
- 配置 wrangler.jsonc:name 为 PROJECT_NAME,compatibility_date 为 "2025-04-01",compatibility_flags 包含 "nodejs_compat",main 必须为 "@tanstack/react-start/server-entry",添加 account_id,d1_databases 绑定 DB、database_name 为 PROJECT_NAME-db、database_id、migrations_dir 为 "drizzle";使用 nodejs_compat 而非 node_compat。
- 配置 tsconfig.json:target 为 ES2022、module 为 ESNext、moduleResolution 为 bundler、jsx 为 react-jsx、strict 为 true、skipLibCheck 为 true、esModuleInterop 为 true、resolveJsonModule 为 true、isolatedModules 为 true、noEmit 为 true、paths 中 @/* 指向 ./src/*、types 为 ["@cloudflare/workers-types/2023-07-01"]、include 为 ["src/**/*", "vite.config.ts"]。
- 生成 .dev.vars:用 openssl rand -hex 32 生成 BETTER_AUTH_SECRET,设置 BETTER_AUTH_URL=http://localhost:3000、TRUSTED_ORIGINS=http://localhost:3000,并按需注释 GOOGLE_CLIENT_ID、GOOGLE_CLIENT_SECRET。
- 创建 .gitignore,包含 node_modules、.wrangler、dist、.output、.dev.vars、.vinxi、.DS_Store。
- 执行 cd PROJECT_NAME && pnpm install;执行 npx wrangler d1 create PROJECT_NAME-db,并把 database_id 复制到 wrangler.jsonc 的 d1_databases 绑定。
- 编写 src/db/schema.ts:包含 better-auth 所需的 users、sessions、accounts、verifications,并添加应用表(如 items)做 CRUD 示例。
- D1 schema 规则:时间戳用 integer(Unix epoch)而非 Date 对象;主键用 text(nanoid/cuid2)而非 autoincrement;单查询绑定参数保持少于 100,大批量插入分批;D1 外键始终开启。
- 编写 src/db/index.ts 的 Drizzle client factory:使用 import { env } from "cloudflare:workers"(不要 process.env),用 drizzle(env.DB, { schema });在每个 server function 内按请求创建 Drizzle client,不要在模块级创建。
- 编写 drizzle.config.ts:schema 为 "./src/db/schema.ts",out 为 "./drizzle",dialect 为 "sqlite"。
- 生成并应用初始迁移:pnpm db:generate;pnpm db:migrate:local。
- 编写 src/lib/auth.server.ts:用 betterAuth + drizzleAdapter(db, { provider: "sqlite" });读取 env.BETTER_AUTH_SECRET、env.BETTER_AUTH_URL、env.TRUSTED_ORIGINS?.split(",") ?? [];启用 emailAndPassword;按需添加 socialProviders Google;getAuth() 必须在每次请求内调用(handler/loader 内),不能在模块级。
- 编写 src/lib/auth.client.ts:用 createAuthClient() from "better-auth/react",导出 useSession、signIn、signOut、signUp。
- 编写 src/routes/api/auth/$.ts:用 createAPIFileRoute("/api/auth/$"),GET/POST 都调用 getAuth().handler(request);better-auth 必须用 API route 而不是 createServerFn。
- Server Functions 核心模式:使用 createServerFn({ method: "GET" }).handler(async () => { const db = getDb(); return db.select().from(items).all(); });总是在 handler 内创建 DB client。
- 用 Zod 做输入校验:createServerFn({ method: "POST" }).inputValidator(z.object({ name: z.string().min(1), description: z.string().optional() })).handler(...);插入时用 crypto.randomUUID() 生成 id,createdAt 用 Date.now()。
- 保护 server functions:写 requireSession(request?):getAuth().api.getSession({ headers: request?.headers ?? new Headers() });无 session 时 throw redirect({ to: "/login" });getSessionFn 返回 auth.api.getSession({ headers: request.headers });getItems 中 await requireSession(request) 后按 session.user.id 过滤。
- 路由 loader 模式:在 createFileRoute("/_authed/items") 中设置 loader: () => getItems(),组件用 Route.useLoaderData() 渲染。
- 认证守卫 _authed.tsx:在 createFileRoute("/_authed") 的 beforeLoad 中 await getSessionFn(),无 session 则 throw redirect({ to: "/login" }),有则 return { session };子路由用 Route.useRouteContext() 获取 session。
- mutation + invalidation:提交后 await createItem({ data }),调用 router.invalidate(),再 router.navigate({ to: "/items" });用 useRouter()。
- 类型安全:用 Drizzle 的 InferSelectModel / InferInsertModel 作为 server function 输入输出类型;认证失败始终 throw redirect(),不要返回错误响应。
- 编写 src/routes/__root.tsx:完整 HTML 文档,使用 @tanstack/react-router 的 <HeadContent /> 和 <Scripts />;<html> 加 suppressHydrationWarning(SSR + theme);内联主题初始化脚本防止闪烁;导入全局 CSS。
- 编写 src/styles/app.css:用 @import "tailwindcss"(v4 语法),在 :root 和 .dark 中放 shadcn/ui CSS 变量;只用语义化 token。
- 编写 src/router.tsx:从 @tanstack/react-router 导入 createRouter as createTanStackRouter,从 "./routeTree.gen" 导入 routeTree,返回 createTanStackRouter({ routeTree }),并 declare module "@tanstack/react-router" 注册 router 类型。
- 编写 src/client.tsx 和 src/ssr.tsx 的标准 TanStack Start 入口样板。
- 安装 shadcn/ui:pnpm dlx shadcn@latest init --defaults;pnpm dlx shadcn@latest add button card input label sidebar table dropdown-menu form separator sheet。
- 实现主题切换:三态 light -> dark -> system -> light,localStorage 持久化,.dark class 加到 <html>;只用 JS 检测系统偏好,不要 CSS @media (prefers-color-scheme)。
- 在 src/components/ 创建 app-sidebar.tsx、theme-toggle.tsx、user-nav.tsx、stat-card.tsx。
- 实现 CRUD server functions:getItems GET 列出当前用户 items;getItem GET 按 ID 取单个;createItem POST 创建;updateItem POST 更新;deleteItem POST 删除。每个函数:获取 auth session、通过 getDb() 创建按请求 Drizzle client、执行 DB 操作、返回带类型数据;route loaders 调 GET,mutations 调 POST 后 router.invalidate()。
- 本地验证:pnpm dev;在 http://localhost:3000 检查应用加载;注册 email/password 账号;登录登出;dashboard 加载 stat cards;创建/列出/编辑/删除 items;主题切换循环 light -> dark -> system;移动端 sidebar 折叠;无 console 错误。
- 部署前检查:wrangler.jsonc 的 account_id 正确;main 为 "@tanstack/react-start/server-entry";compatibility_flags 含 nodejs_compat;D1 database 已创建且 database_id 已设置;.dev.vars 已 gitignore;源码无硬编码 secrets。
- 设置生产 secrets:openssl rand -hex 32 | npx wrangler secret put BETTER_AUTH_SECRET;echo "https://PROJECT.SUBDOMAIN.workers.dev" | npx wrangler secret put BETTER_AUTH_URL;echo "http://localhost:3000,https://PROJECT.SUBDOMAIN.workers.dev" | npx wrangler secret put TRUSTED_ORIGINS;如用 Google OAuth,echo "your-client-id" | npx wrangler secret put GOOGLE_CLIENT_ID 和 echo "your-client-secret" | npx wrangler secret put GOOGLE_CLIENT_SECRET。
- 如用 Google OAuth,在 Google Cloud Console 添加生产 redirect URI:https://PROJECT.SUBDOMAIN.workers.dev/api/auth/callback/google。
- 迁移并部署:pnpm db:migrate:remote;pnpm build && npx wrangler deploy。
- 首次部署后,把 BETTER_AUTH_URL 更新为实际 Worker URL 并重新部署。
- 部署后验证:生产 URL 能加载;auth 可用;CRUD 可用;theme 持久化。
- 自定义域名(可选):Cloudflare Dashboard -> Workers -> Triggers -> Custom Domains;更新 BETTER_AUTH_URL + TRUSTED_ORIGINS secrets + Google OAuth redirect URI 到新域名;重新部署。
- 修复 env undefined:不要模块级访问 env,只在 request handler 内 import { env } from "cloudflare:workers"。
- 修复 D1 database not found:检查 wrangler.jsonc 的 d1_databases binding 名称与代码一致。
- 修复 auth redirect loop:BETTER_AUTH_URL 必须与实际 URL 完全匹配(协议+域名,无尾斜杠)。
- 修复 auth silently fails:设置 TRUSTED_ORIGINS secret,包含所有有效 URL,逗号分隔。
- 修复 styles not loading:确保 vite.config.ts 中有 @tailwindcss/vite 插件。
- 修复 SSR hydration mismatch:给 <html> 添加 suppressHydrationWarning。
- 修复 Build fails on Cloudflare:检查 nodejs_compat flag 和 wrangler.jsonc 的 main 字段。
- 修复 Secrets not taking effect:wrangler secret put 不会重新部署,之后要运行 npx wrangler deploy。
- 修复 Auth endpoints return 404:better-auth 用 createAPIFileRoute(API route),不要用 createServerFn。
- 修复 "redirect_uri_mismatch":把生产 URL 加到 Google Cloud Console OAuth redirect URIs。
- 修复 Cryptic Vite errors:插件顺序必须为 cloudflare() -> tailwindcss() -> tanstackStart() -> viteReact()。
- 修复 "Table not found" 500s:部署前运行 pnpm db:migrate:remote。
适用边界 · 注意事项
- 不适合非 Cloudflare Workers 运行时的 TanStack Start 项目;本流程依赖 Workers、D1、wrangler secrets。
- 不适合已有项目增量改造,或不想从零逐文件生成、没有 Cloudflare 账号/D1 的场景。
- 不适用只需纯前端、静态站点或不用 SSR/Server Functions/better-auth 的任务。