Wasp kitchen-sink 示例应用全解析:Wasp CLI 开发测试平台与全特性展示场
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
导读
examples/kitchen-sink是 Wasp 仓库中一个"样样俱全"的示例应用:它既是 Wasp 团队为 contributors 准备的低成本开发测试平台,也是向开发者集中展示 Wasp 全栈框架各项能力的演示工程。本文将带你梳理这个应用的定位、初始化流程、环境变量管理、开发/运行/测试全链路,并深入到其 TypeScript 配置与源码实现中,逐一解读认证、查询/动作、后台任务、CRUD、自定义 API、数据库种子、WebSocket 等特性在真实工程里是如何声明的。读完你既能快速把它跑起来,也能把它当作 Wasp 官方功能的"活字典"按图索骥。
kitchen-sink 的设计目标:既是测试床,也是特性清单
从 README.md 开篇可以明确看到,这个应用承担两大使命:
- 首要目的:让 Wasp 贡献者轻松地测试开发版本的 Wasp CLI。Wasp 的 CLI(Haskell 实现,位于 waspc/ 目录)在迭代过程中需要不断验证
wasp start、wasp db migrate-dev、wasp build等命令的可用性,kitchen-sink 就是那个随时可以拿来"跑一遍"的靶场。 - 次要目的:尽可能多地演示 Wasp 特性。README 特别说明:并非所有特性都能同时共存(例如
email与usernameAndPassword两种认证方式互斥),但在约束范围内应尽可能覆盖。
此外,它是 Waspe2e 应用测试的主战场——任何新增或修改的特性,都应尽量在这个应用里补充对应的端到端测试。这一点在 e2e-tests/tests/ 目录的 19 个 spec 文件中得到印证(auth.spec.ts、crud.spec.ts、jobs、websocket.spec.ts等),我们将在后文详述。
项目结构:一个用 TypeScript 声明一切的应用
kitchen-sink 采用 Wasp 较新的 TypeScript 定义方式:核心配置集中在 main.wasp.ts,业务代码按特性(feature)分目录组织在 src/features/ 下,每个特性通过独立的*.wasp.ts文件导出自己的声明片段,再由main.wasp.ts汇总。
// main.wasp.ts(节选) import { app, page, route } from "@wasp.sh/spec"; // ... export default app({ name: "KitchenSink", wasp: { version: "0.26.0" }, title: "Wasp Kitchen Sink", webSocket, auth: authConfig, server: { setupFn: serverSetup, middlewareConfigFn: serverMiddlewareFn, envValidationSchema: serverEnvValidationSchema, }, client: { rootComponent: App, setupFn: clientSetup, envValidationSchema: clientEnvValidationSchema, }, db, emailSender: { provider: "SMTP", defaultFrom: { email: "kitchen-sink@wasp.sh" } }, spec: [ /* route、page、authSpec、operationsSpec、jobsSpec、apisSpec、crudSpec ... */ ], });从这段声明可以看到一个完整的 Wasp 应用骨架:应用级配置(name/wasp版本/head标签)、服务端与客户端的setupFn、中间件配置函数、环境变量校验 schema、数据库配置、SMTP 邮件发送器,以及按特性拆分的路由与能力清单spec。
按特性组织的 src 目录
src/features/ ├── apis/ 自定义 HTTP API 与命名空间 ├── auth/ 多 Provider 认证(Google/GitHub/Discord/Slack/Microsoft/Email) ├── chat/ WebSocket 实时通信 ├── crud/ CRUD 操作与覆盖函数 ├── db/ Prisma 设置与数据库种子 ├── jobs/ PgBoss 后台任务与定时任务 ├── lazy-loading/ 懒加载路由演示 ├── operations/ Query/Action 与序列化演示 ├── prerender/ 静态预渲染演示 └── streaming/ Server-Sent Events 流式响应这种"一个特性一个目录 + 一个*.wasp.ts声明文件"的组织方式,本身就是对 Wasp"以声明式代码驱动全栈能力"理念的最佳示范。
初始化与依赖安装
首次拿到仓库后,进入 kitchen-sink 目录执行依赖安装:
wasp install该命令会基于 main.wasp.ts 中的wasp: { version: "0.26.0" }校验 CLI 版本兼容性,并为项目生成.wasp输出目录。项目本身是 npm workspace 结构,package.json 中声明了workspaces: [".wasp/out/*", ".wasp/out/sdk/wasp"],即把 Wasp 生成的前端、后端与 SDK 包作为本地 workspace 链接,保证运行时依赖与生成代码始终一致。
环境变量:从最小启动到团队共享
最小启动:复制示例文件
kitchen-sink 提供了带假值的示例环境变量文件,最小化配置即可启动:
cp .env.server.example .env.serverREADME 明确说明:这样创建的.env.server中各项变量都是 dummy 值,应用可以正常启动,但并非所有特性都能工作——例如 Google Auth、GitHub Auth 等第三方 Provider 需要真实的 API Key 才能完成 OAuth 流程。配置这些 Provider 的细节可查阅 Wasp 官方文档中对应章节。
团队共享:dotenv-vault
对于 Wasp 团队成员,kitchen-sink 通过 package.json 中的脚本封装了 dotenv-vault(外部工具,此处仅说明命令本身):
# 拉取共享的 .env.server(含真实 API Keys) npm run env:pull # 将本地改动同步回共享配置 npm run env:push对应的脚本定义为:
"env:pull": "npx dotenv-vault@latest pull development .env.server", "env:push": "npx dotenv-vault@latest push development .env.server"这套机制解决了"本地开发需要真实密钥、但密钥不应进入 Git"的矛盾,适合团队协作场景。
环境变量的运行时校验
kitchen-sink 还示范了 Wasp 的环境变量校验能力。src/env.ts 使用 Zod 分别定义了服务端与客户端的校验 schema:
import { defineEnvValidationSchema } from "wasp/env"; import * as z from "zod"; export const serverEnvValidationSchema = defineEnvValidationSchema( z.object({ TEST_ENV_VAR: z.string({ error: "TEST_ENV_VAR is required." }), }), ); export const clientEnvValidationSchema = defineEnvValidationSchema( z.object({ REACT_APP_NAME: z.string().default("Kitchen Sink App"), }), );可以看到:服务端变量TEST_ENV_VAR缺失时会抛出自定义错误信息;客户端变量REACT_APP_NAME则提供了默认值"Kitchen Sink App"。这两个 schema 分别挂在 main.wasp.ts 的server.envValidationSchema与client.envValidationSchema上,在应用启动阶段即完成校验,属于"配置即验证"的实践。
运行应用:三步启动
kitchen-sink 的运行方式与任何 Wasp 应用完全一致,README 给出了标准流程:
1. 启动数据库(独立终端):
wasp start db该命令拉起 Postgres 容器。从 schema.prisma 可以看到数据库 provider 固定为postgresql,DATABASE_URL由环境变量注入。
2. 迁移数据库(如需要,另一个终端):
wasp db migrate-devkitchen-sink 的迁移记录都在 migrations/ 目录下,涵盖初始表、地址字段、会话、投票、可见性枚举、邮件验证钩子、大写文本任务等多次演进。以 schema.prisma 当前模型为例,核心实体包括:
User:除基础字段外,还带有isOnAfterSignupHookCalled、isOnAfterLoginHookCalled、isOnAfterEmailVerifiedHookCalled、numTimesOnAfterEmailVerifiedCalled等钩子调用记录字段——它们正是 e2e 测试中验证认证钩子是否被正确触发的依据;Task:含TaskVisibility枚举(PRIVATE/LINK_ONLY/PUBLIC),演示枚举类型在查询与 CRUD 中的应用;TaskVote:多对多关系的投票表;UppercaseTextRequest:配合后台任务演示"提交请求 → 异步处理 → 查询结果"的完整链路。
3. 启动应用(第三个终端):
wasp start4. 浏览器打开localhost:3000,即可看到应用首页。
使用开发版 Wasp CLI:waspc/run 脚本
kitchen-sink 的首要定位是测试开发版Wasp CLI,因此 README 特别给出了三种调用方式:
# 从 kitchen-sink 目录内,使用仓库内脚本的相对路径: ../../waspc/run wasp-cli start db # 或为该脚本设置别名后使用绝对路径: wrun wasp-cli start db # 或将开发版 waspc 二进制全局安装后直接调用: wasp-cli start db这里的 waspc/run 脚本是 Wasp 仓库内为 contributors 准备的开发用包装器,它会构建(或复用已构建的)waspc Haskell 可执行文件并转发后续参数,从而让你总是用最新源码的 CLI 行为来驱动 kitchen-sink,而不是已发布的正式版本。这对贡献者验证 CLI 改动、排查回归至关重要:任何一个start、build、db子命令的改动,都可以在 kitchen-sink 上立刻得到真实反馈。
全特性深度解读:从声明到实现
kitchen-sink 的价值在于它几乎覆盖了 Wasp 的每一项核心能力。下面逐特性拆解其声明文件与配套实现。
1. 认证:六种 Provider + 完整生命周期钩子
src/features/auth/auth.wasp.ts 是仓库里信息量最大的声明文件之一,展示了 Wasp 认证体系的全貌:
- 五种 OAuth Provider:
slack、discord、google、gitHub、microsoft,每个 Provider 都带有configFn(读取对应环境变量、组装 OAuth 配置)与userSignupFields(声明注册时需要收集的额外用户字段,对应实现位于 providers/); - 邮箱密码认证:
email方法配置了fromField、邮件验证(getEmailContentFn+ 客户端验证路由EmailVerificationRoute)和密码重置(getPasswordResetContentFn+PasswordResetRoute); - 认证钩子:
onBeforeSignup、onAfterSignup、onAfterEmailVerified、onBeforeLogin、onAfterLogin,实现见 hooks.ts。这些钩子会修改 User 实体上那些isOnAfter*HookCalled字段,供 auth-hooks.spec.ts 端到端验证; - 自定义注册流程:
/custom-signup(自定义 Signup 页面 + 自定义 action,见 customSignup.ts)与/manual-signup(手动注册页)两条演示路由,对应 custom-signup.spec.ts 与 manual-signup.spec.ts; - 跳转策略:
onAuthFailedRedirectTo: "/login"、onAuthSucceededRedirectTo: "/"。
认证相关路由(注册、登录、密码重置、邮件验证、Profile 页)统一由authSpec导出并在main.wasp.ts中注册,其中/profile页使用了{ authRequired: true }保护。
2. Query / Action:类型安全的 RPC 与实体跟踪
src/features/operations/operations.wasp.ts 展示了 Wasp 的 RPC 核心:
query(getTasks, { entities: ["Task"] }), query(getNumTasks, { entities: ["Task"], auth: false }), action(createTask, { entities: ["Task"] }), action(updateTaskIsDone, { entities: ["Task"] }), // ... query(getSerializedObjects),query/action的entities字段声明操作涉及的数据模型,Wasp 据此生成自动缓存失效逻辑——相关的测试见 cacheInvalidation.test.ts;auth: false可标记无需认证的公开查询(如getNumTasks);- 页面路由(
/tasks、/tasks/:id)都带authRequired: true; - 特别地,
getSerializedObjects与 SerializationPage.tsx 用来演示 Wasp 对 Date、特殊对象等复杂类型在客户端/服务端之间的序列化处理,配套 serialization.spec.ts 测试。
3. 后台任务:PgBoss 执行器与 Cron 调度
src/features/jobs/jobs.wasp.ts 覆盖了 Wasp 后台任务的两个核心场景:
job(uppercaseTextJob, { executor: "PgBoss", entities: ["UppercaseTextRequest"], }), job(mySpecialJob, { executor: "PgBoss", performExecutorOptions: { pgBoss: { retryLimit: 1 } }, entities: ["Task"], }), job(mySpecialScheduledJob, { executor: "PgBoss", schedule: { cron: "0 * * * *", args: { foo: "bar" }, executorOptions: { pgBoss: { retryLimit: 2 } }, }, }),- 按需任务:
uppercaseTextJob接收一个UppercaseTextRequest记录,异步完成文本大写转换并回写结果——配套 uppercaseText.ts 与 JobsPage; - 任务级重试策略:
mySpecialJob通过performExecutorOptions.pgBoss.retryLimit设置重试次数; - 定时任务:
mySpecialScheduledJob用cron: "0 * * * *"声明每小时执行,并携带静态参数args与调度级重试配置executorOptions。
从源码结构看,所有后台任务均指定executor: "PgBoss",这意味着 kitchen-sink 的 job 依赖 Postgres(与 schema.prisma 的UppercaseTextRequestState枚举PENDING/SUCCESS/ERROR共同构成任务状态机),对应 e2e 测试为 async-jobs.spec.ts。
4. CRUD:声明式增删改查 + 覆盖函数 + 部分 CRUD
src/features/crud/crud.wasp.ts 演示了三种 CRUD 用法:
crud("tasks", "Task", { get: {}, getAll: { overrideFn: crudGetAllTasks }, create: { overrideFn: crudCreateTask }, update: {}, delete: {}, }), // 故意只声明 getAll:覆盖"用户未请求的操作不应出现在生成代码中"的情形 crud("taskVotes", "TaskVote", { getAll: {} }),- 一个
crud声明即可为指定实体生成get/getAll/create/update/delete全套操作; overrideFn允许用自定义函数替换默认实现(例如在创建任务前补充业务逻辑),实现在 crud.ts;taskVotes示例故意只声明getAll,用于验证 Wasp 生成的代码不会包含用户未请求的操作——这是对"最小生成面"的测试用例,属于 contributor 场景下的边界验证。
5. 自定义 API:方法、命名空间与中间件
src/features/apis/apis.wasp.ts 覆盖了自定义 REST API 的声明方式:
api("ALL", "/foo/bar", fooBar, { middlewareConfigFn: fooBarMiddlewareFn, entities: ["Task"], }), apiNamespace("/bar", { middlewareConfigFn: barNamespaceMiddlewareFn }), api("GET", "/bar/baz", barBaz, { auth: false, entities: ["Task"] }), api("POST", "/webhook/callback", webhookCallback, { middlewareConfigFn: webhookCallbackMiddlewareFn, auth: false, }),api声明支持指定 HTTP 方法(ALL/GET/POST)、路径、处理函数与可选配置;apiNamespace可以为路径前缀统一注入中间件(/bar下的所有 API 共享barNamespaceMiddlewareFn);auth: false用于公开端点(如 webhook 回调);entities声明同样驱动缓存失效;- 中间件实现位于 apis.ts,配套测试 custom-apis.spec.ts。
6. 数据库:种子数据与 Prisma 设置函数
src/features/db/db.wasp.ts 演示了数据层的两个扩展点:
export const db: Db = { seeds: [devSeedSimple, prodSeed], prismaSetupFn: setUpPrisma, };seeds区分开发种子(devSeedSimple)与生产种子(prodSeed),实现见 seeds.ts;prismaSetupFn允许在 PrismaClient 创建时注入自定义设置(如日志、扩展),实现见 prisma.ts,并有 prisma-setup-fn.spec.ts 做端到端验证。
7. 其他特性:WebSocket、Streaming、预渲染、懒加载
main.wasp.ts的spec中还导入了其余特性:
- WebSocket:来自 chat.wasp.ts,通过
webSocket配置与 webSocket.ts 实现实时聊天,测试见 websocket.spec.ts; - Streaming(SSE):streaming.wasp.ts 声明流式 API,StreamingTestPage.tsx 展示逐块接收响应,测试见 streaming.spec.ts;
- 预渲染:prerender.wasp.ts 覆盖静态预渲染、带参数的预渲染实例与 hydration mismatch 场景,对应 prerender.spec.ts;
main.wasp.ts中route("HomeRoute", "/", page(HomePage), { prerender: true })即首页预渲染示例; - 懒加载:lazyLoading.wasp.ts 演示 Eager/Lazy 两种加载模式,测试见 lazy-loading.spec.ts;
- RPC 类型安全专项:src/rpcTests/ 通过 TS 与 JS 两套定义(definitions.ts 与 jsDefinitions.js)对比验证 Wasp 生成的 RPC 客户端在两种语言下的类型/行为一致性。
端到端测试:Playwright 双视口矩阵
kitchen-sink 的 e2e 测试是 Wasp 验证框架行为的主要手段。测试入口为:
npm run test该脚本实际执行(见 package.json):
"test": "npm run test:install-deps && DEBUG=pw:webserver playwright test --config e2e-tests/", "test:install-deps": "playwright install --with-deps"Playwright 配置要点
playwright.config.ts 中值得注意的设计:
- 两个测试项目:
chromium(桌面 Chrome)与Mobile Chrome(Pixel 5 视口),覆盖响应式场景; - webServer 自动拉起:通过
WASP_APP_RUNNER_CLI_CMD、WASP_RUN_MODE(默认dev)、WASP_CLI_CMD(默认wasp-cli)三个环境变量拼出启动命令run-wasp-app dev --path-to-app=../ --wasp-cli-cmd=wasp-cli,等待localhost:3001就绪后开始测试,超时 180 秒;reuseExistingServer: !process.env.CI允许本地复用已运行的服务; - 部署模式:当
WASP_RUN_MODE=deployed时不再自启 webServer,而是测试已部署的实例(PLAYWRIGHT_SERVER_URL指定地址); - CI 行为:
forbidOnly、retries: 2、单 worker、dotreporter 均为 CI 环境专用。
测试覆盖清单
e2e-tests/tests/ 下的 19 个 spec 文件与上文特性一一对应:auth.spec.ts、auth-hooks.spec.ts、custom-signup.spec.ts、manual-signup.spec.ts、user-api.spec.ts、operations.spec.ts、serialization.spec.ts、async-jobs.spec.ts、crud.spec.ts、custom-apis.spec.ts、websocket.spec.ts、streaming.spec.ts、prerender.spec.ts、lazy-loading.spec.ts、catch-all-route.spec.ts、prisma-setup-fn.spec.ts等,另有 mailcrab.ts 用于测试邮件类功能(验证邮件、密码重置邮件)。这让 kitchen-sink 成为名副其实的"特性回归测试中心"。
小结:如何用好 kitchen-sink
综合来看,kitchen-sink 的三种典型用法对应三条路径:
- 作为普通 Wasp 开发者:复制
.env.server.example即可wasp install+wasp start db+wasp db migrate-dev+wasp start全流程跑通,把首页当作特性导航,逐个进入/tasks(Query/Action)、/crud(CRUD)、/jobs(后台任务)、/apis(自定义 API)、/chat(WebSocket)、/serialization(序列化)、预渲染与懒加载页面,感受 Wasp"声明即所得"的开发体验; - 作为 Wasp 贡献者:通过
../../waspc/run wasp-cli <子命令>用源码级 CLI 驱动应用,任何 CLI 改动都能立刻在此验证;新增特性时同步补充*.wasp.ts声明、源码实现与 e2e 测试; - 作为测试基础设施:
npm run test一键跑完双视口 e2e 矩阵,配合env:pull/env:push共享真实密钥的团队工作流,构成 Wasp 仓库内闭环的质量保障体系。
无论从哪个角度切入,examples/kitchen-sink 都是理解 Wasp 框架能力边界与工程实践的最佳入口。
【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考