Wasp 服务端配置实战:setupFn 与 middlewareConfigFn 完全指南
Wasp 服务端配置实战setupFn 与 middlewareConfigFn 完全指南【免费下载链接】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导读在 Wasp 中几乎所有全栈能力鉴权、数据库、Jobs、RPC都是通过声明式配置自动生成的但真实的业务场景总有框架默认行为覆盖不到的地方——比如自定义一条 HTTP 路由、在服务启动时初始化第三方资源、或者为所有 API 调整 CORS 与请求体解析策略。本文基于 Wasp 仓库中 server-config 文档当前最新版见 web/docs/project/server-config.md系统讲解app声明中server字段的两个核心配置项setupFn与middlewareConfigFn从配置语法、参数签名到结合源码的底层执行原理与真实项目如 examples/kitchen-sink的落地示例。读完本文你将掌握在 Wasp 服务端启动阶段注入自定义逻辑、为全局 Operations/APIs 定制中间件栈的完整方法。一、server 字段配置服务端行为的入口Wasp 中服务端的全部自定义配置都收敛在app声明的server字段里。在 0.14 版本使用main.wasp配置文件中声明方式如下app MyApp { title: My app, // ... server: { setupFn: import { mySetupFunction } from src/myServerSetupCode.js, middlewareConfigFn: import { myMiddlewareConfigFn } from src/myServerSetupCode.js } }server是一个字典包含以下字段字段类型说明setupFnExtImport外部导入服务启动时执行的异步函数await完成后服务器才开始接收请求middlewareConfigFnExtImport外部导入全局 Express 中间件配置函数影响所有 Operations 与 APIs这一结构在源码层面得到了印证在 waspc/src/Wasp/AppSpec/App/Server.hs 中Server数据类型正是由setupFn :: Maybe ExtImport与middlewareConfigFn :: Maybe ExtImport两个可选项构成此外还有一个envValidationSchema字段用于服务端环境变量校验而 waspc/src/Wasp/AppSpec/App.hs 中app声明通过server :: Maybe Server挂载这一配置。也就是说这两个配置项最终都会被 Wasp 编译器读取并注入到生成的 Express 服务端代码中。注本文以 0.14 版本文档为主线当前主线版本0.26已改用main.wasp.tswasp.sh/spec的 TS 写法二者配置语义完全一致见后文对比。二、setupFn在服务启动时执行自定义逻辑2.1 它是什么setupFn声明一个将在服务器启动时执行的 TypeScript或 JavaScript函数。这个函数必须是异步的Wasp 会在服务器开始接受任何请求之前await它完成——这意味着你可以在其中安全地准备数据库连接、WebSocket 服务、启动定时任务cron / scheduled jobs或初始化第三方 SDK而不用担心请求到达时资源尚未就绪。setupFn会收到一个上下文对象其中包含两个实例app即express.Application实例server即http.Server实例。在 TypeScript 中其完整类型签名定义于wasp/server模块export type ServerSetupFn (context: ServerSetupFnContext) Promisevoid export type ServerSetupFnContext { app: Application // express.Application server: Server // http.Server }2.2 实际示例一添加自定义路由最常见的用法之一是利用app实例添加框架未声明的自定义路由import { ServerSetupFn } from wasp/server import { Application } from express export const mySetupFunction: ServerSetupFn async ({ app }) { addCustomRoute(app) } function addCustomRoute(app: Application) { app.get(/customRoute, (_req, res) { res.send(I am a custom route) }) }JavaScript 版本写法相同仅去掉类型注解export const mySetupFunction async ({ app }) { addCustomRoute(app) } function addCustomRoute(app) { app.get(/customRoute, (_req, res) { res.send(I am a custom route) }) }2.3 实际示例二存储值供 Operations 后续使用如果希望把启动阶段产生的资源保存下来供后续 Query / Action即 Wasp 的 Operations读取可以在setupFn所在模块中用模块级变量存储并导出读取函数。由于 Node.js 模块天然是单例这等价于把模块本身当作在服务启动时构造的单例。import { type ServerSetupFn } from wasp/server let someResource undefined export const mySetupFunction: ServerSetupFn async () { // 假设 setUpSomeResource 与 startSomeCronJob // 在本文件下方实现或从其他文件导入。 someResource await setUpSomeResource() startSomeCronJob() } export const getSomeResource () someResourceimport { type SomeQuery } from wasp/server/operations import { getSomeResource } from ./myServerSetupCode.js ... export const someQuery: SomeQuery... async (args, context) { const someResource getSomeResource() return queryDataFromSomeResource(args, someResource) }import { getSomeResource } from ./myServerSetupCode.js ... export const someQuery async (args, context) { const someResource getSomeResource() return queryDataFromSomeResource(args, someResource) }推荐做法把变量放在定义 setup 函数的同一模块中再导出读取函数供 Operations 直接 import 使用——这样该模块就变成了一个在服务启动时完成构造的单例比把值塞进全局对象或 DB 更简洁可控。2.4 源码视角setupFn 的调用链从生成器模板可以清楚看到setupFn在启动流程中的精确位置。在 waspc/data/Generator/templates/server/src/server.ts 中生成的服务器启动函数大致如下const startServer async () { // 若使用了 PgBoss 任务队列先启动它 await startPgBoss() const server http.createServer(app) // 用户自定义 setupFn 在这里被调用 const serverSetupFnContext: ServerSetupFnContext { app, server } await (setupFn)(serverSetupFnContext) // WebSocket 初始化等 await initWebSocket(server) server.listen(port) }可以确认两个关键事实其一setupFn收到的是{ app, server }上下文与文档中的类型签名完全一致其二它在server.listen()之前被await从源码层面印证了服务器在 setupFn 完成前不会开始接受请求这一行为。这为启动期的资源初始化提供了可靠的时间窗口。另外在 examples/kitchen-sink/src/serverSetup.ts 中官方示例综合展示了这一模式先通过app注册自定义路由/customRoute模拟耗时 2 秒的资源初始化后写入someResource并在启动阶段直接提交一个 Wasp JobmySpecialJob.submit同时读取环境变量env.TEST_ENV_VAR——这说明setupFn是启动期一次性的全部自定义逻辑的挂载点包括 Job 提交与配置读取。三、middlewareConfigFn全局中间件定制3.1 它是什么middlewareConfigFn指向一个 Express 中间件配置函数用于全局修改中间件栈其影响范围是所有 OperationsQuery/Action与所有 API。任何新增、删除或替换都会全局生效因此文档与源码均强调修改全局中间件必须极其谨慎若不确认影响面优先使用按 API 或按路径apiNamespace粒度的定制方式。在main.wasp中的声明方式0.14app MyApp { title: My app, // ... server: { setupFn: import { mySetupFunction } from src/myServerSetupCode.js, middlewareConfigFn: import { myMiddlewareConfigFn } from src/myServerSetupCode.js } }3.2 Wasp 默认的全局中间件Wasp 为每个应用预置了一套精简而实用的 Express 中间件。中间件以Mapstring, RequestHandler即MiddlewareConfig的形式组织键名即标识。默认配置定义在生成器模板 waspc/data/Generator/templates/server/src/middleware/globalMiddleware.ts 中const defaultGlobalMiddlewareConfig: MiddlewareConfig new Map([ [helmet, helmet()], [cors, cors({ origin: config.allowedCORSOrigins })], [logger, logger(dev)], [express.json, express.json()], [express.urlencoded, express.urlencoded()], [cookieParser, cookieParser()] ])键名中间件作用helmetHelmet通过设置各类 HTTP 响应头加固 Express 应用安全性corscors启用 CORS允许前端与后端通信默认origin取自config.allowedCORSOriginsloggermorganHTTP 请求日志记录dev格式express.jsonbody-parser解析 JSON 请求体结果挂到req.bodyWasp Operations 依赖它正常工作express.urlencodedbody-parser解析application/x-www-form-urlencoded请求体cookieParsercookie-parser解析Cookie头并填充req.cookies中间件类型定义同样可以从模板中看到export type MiddlewareConfig Mapstring, express.RequestHandler export type MiddlewareConfigFn (middlewareConfig: MiddlewareConfig) MiddlewareConfigMiddlewareConfigFn接收当前的MiddlewareConfig一个 Map返回修改后的新 MapWasp 会按其插入顺序装配成 Express 中间件数组。3.3 中间件配置函数的执行原理在 globalMiddleware.ts 模板中用户自定义的middlewareConfigFn会被应用于默认配置生成全局中间件当某个路由需要进一步定制时globalMiddlewareConfigForExpress会先克隆全局 Map 再交给路由级配置函数修改避免污染全局配置const globalMiddlewareConfig: MiddlewareConfig middlewareConfigFn(defaultGlobalMiddlewareConfig) export function globalMiddlewareConfigForExpress(middlewareConfigFn?: MiddlewareConfigFn): express.RequestHandler[] { if (!middlewareConfigFn) { return Array.from(globalMiddlewareConfig.values()) } // 克隆防止某个路由的修改影响其他路由 const globalMiddlewareConfigClone new Map(globalMiddlewareConfig) const modifiedMiddlewareConfig middlewareConfigFn(globalMiddlewareConfigClone) return Array.from(modifiedMiddlewareConfig.values()) }这一实现与文档描述完全一致middlewareConfigFn是在默认中间件基础上的增量修改你可以用set覆盖、用delete移除、用set新增。3.4 实战为 CORS 追加多个域名最常见的全局中间件定制需求是扩展 CORS 允许来源。0.14 版本的配置函数示例如下当前主线版本写法相同import cors from cors import { config, type MiddlewareConfigFn } from wasp/server export const serverMiddlewareFn: MiddlewareConfigFn (middlewareConfig) { // 在默认允许来源基础上追加额外域名 middlewareConfig.set( cors, cors({ origin: [...config.allowedCORSOrigins, https://example1.com, https://example2.com] }) ) return middlewareConfig }config.allowedCORSOrigins是 Wasp 从配置中解析出的默认允许来源数组将其展开后再追加新域名即可做到默认 新增双兼容。config与env都从wasp/server导入具体用法可参考 examples/kitchen-sink/src/serverSetup.ts其中正是通过该模式将http://127.0.0.1:3000加入 CORS 白名单。若需要按 API如POST /webhook/callback单独替换express.json为express.raw或按路径apiNamespace做更细粒度的中间件定制Wasp 在middlewareConfigFn之外还提供了 per-api 与 per-path 两档定制能力详见 web/docs/advanced/middleware-config.md本文不再展开。四、当前主线版本的写法main.wasp.tsWasp 当前主线0.26已迁移到 TypeScript 规范TS Spec使用wasp.sh/spec的app()函数与with { type: ref }导入声明。server字段的语义与 0.14 完全一致仅语法不同import { app } from wasp.sh/spec import { myMiddlewareConfigFn, mySetupFunction } from ./src/myServerSetupCode with { type: ref } export default app({ name: MyApp, server: { setupFn: mySetupFunction, middlewareConfigFn: myMiddlewareConfigFn, }, // ... })真实仓库中 examples/kitchen-sink/main.wasp.ts 与 examples/waspleau/main.wasp.ts 均采用此写法。此外该版本还可在server字段中声明envValidationSchema用于服务端环境变量校验完整字段清单可查阅 web/docs/project/server-config.md 的 API Reference。五、API 参考速查setupFn: ExtImport声明一个在服务器启动时执行的异步函数会被await完成后服务器才开始接受请求上下文为{ app: express.Application, server: http.Server }典型用途初始化数据库/WebSocket、启动 cron/定时任务、注册自定义路由、预提交 Job在 TS 中建议标注类型ServerSetupFn从wasp/server导入。middlewareConfigFn: ExtImport指向一个(middlewareConfig: MiddlewareConfig) MiddlewareConfig函数全局修改中间件栈影响范围所有 Operationsquery 与 action以及所有 API基于默认中间件 Maphelmet、cors、logger、express.json、express.urlencoded、cookieParser做增量修改在 TS 中建议标注类型MiddlewareConfigFn从wasp/server导入更细粒度的定制请参考 中间件配置文档 中的 per-api 与 per-pathapiNamespace方案。结语setupFn与middlewareConfigFn是 Wasp 服务端框架默认行为与业务自定义需求之间的两扇门前者让你在进程启动、对外服务之前完成一切资源准备后者让你在不放弃 Operations/API 自动装配能力的前提下按需调整全局中间件。结合 server.ts 与 globalMiddleware.ts 的生成模板以及 kitchen-sink 示例你可以清晰地把声明式配置映射到实际生成的 Express 代码从而在需要深度定制时依然保持对运行行为的精确掌控。【免费下载链接】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),仅供参考