Wasp 服务端配置详解:通过 app.server 的 setupFn 与 middlewareConfigFn 定制 Express 启动流程与全局中间件

Wasp 服务端配置详解:通过 app.server 的 setupFn 与 middlewareConfigFn 定制 Express 启动流程与全局中间件 Wasp 服务端配置详解通过 app.server 的 setupFn 与 middlewareConfigFn 定制 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本文基于 Wasp 官方文档《Server Config》web/versioned_docs/version-0.13/project/server-config.md展开系统讲解app声明中server字段的两个配置项——setupFn与middlewareConfigFn——的用法、执行时机与底层实现机制。读完后你将能够在 Wasp 应用的 Node.js 服务启动阶段注册自定义路由、预初始化外部资源数据库连接、定时任务等并安全地修改作用于所有 Operations 与 API 的全局 Express 中间件栈同时理解 Wasp 代码生成器是如何把你的配置函数织入到生成代码中的。一、server 字段总览你可以通过app声明的server字段来配置服务端的运行行为。完整的声明形式如下JavaScript 与 TypeScript 两种 spec 语法写法一致// main.wasp app MyApp { title: My app, // ... server: { setupFn: import { mySetupFunction } from src/myServerSetupCode.js, middlewareConfigFn: import { myMiddlewareConfigFn } from src/myServerSetupCode.js } }server是一个字典包含两个字段setupFn: ExtImport与middlewareConfigFn: ExtImport。从源码结构看这个字典在 Wasp 编译器waspc中对应一个独立的 Haskell 数据类型定义于 Server.hs其结构为data Server Server { setupFn :: Maybe ExtImport, middlewareConfigFn :: Maybe ExtImport, envValidationSchema :: Maybe ExtImport }两个字段均为Maybe ExtImport即都是可选的——你可以只配置其中之一也可以都不配置。可以推断该数据结构中的envValidationSchema字段是后续版本在server配置上新增的能力0.13 版本文档中仅列出setupFn与middlewareConfigFn两项。以下两节分别深入讲解这两个字段的实际用法。二、setupFn服务启动函数setupFn声明一个 JavaScript/TypeScript 函数该函数会在服务器启动时执行。它的典型用途包括注册自定义 Express 路由、预初始化某些资源额外的数据库连接、WebSocket、第三方客户端等、启动 cron/定时任务。官方文档给出的一个典型例子是添加一条自定义路由JavaScript 版// src/myServerSetupCode.js export const mySetupFunction async ({ app }) { addCustomRoute(app) } function addCustomRoute(app) { app.get(/customRoute, (_req, res) { res.send(I am a custom route) }) }TypeScript 版// src/myServerSetupCode.ts 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) }) }2.1 函数上下文express.Application 与 http.ServersetupFn的函数体会接收一个上下文对象其中包含express.Application与http.Server两个实例这对需要挂接自定义服务器逻辑的场景非常有用。TypeScript 中对应的公开类型定义来自 Wasp SDK 的wasp/server模块其源码位于 types/index.ts// wasp/server export type ServerSetupFn (context: ServerSetupFnContext) Promisevoid export type ServerSetupFnContext { app: Application // express.Application server: Server // http.Server }因此一个最简的 TypeScript 启动函数可以写成// src/myServerSetupCode.ts import { type ServerSetupFn } from wasp/server export const mySetupFunction: ServerSetupFn async () { await setUpSomeResource() }2.2 执行时机在开始接收请求之前 awaitAPI 参考明确指出setupFn被期望是一个 async 函数并且 Wasp 会await 它完成之后才开始接受任何请求。这一点在生成器模板中可以找到直接证据。Wasp 编译时为每个应用生成服务端入口文件其模板位于 server.ts核心启动流程是const startServer async () { const port normalizePort(config.port) app.set(port, port) const server http.createServer(app) // 只有定义了 setupFn 时下面这段代码才会被生成 const serverSetupFnContext: ServerSetupFnContext { app, server } await ({ setupFn.importIdentifier } as ServerSetupFn)(serverSetupFnContext) server.listen(port) // ...错误处理EACCES / EADDRINUSE 等 }从这段模板可以看出三件事setupFn在http.createServer(app)之后、server.listen(port)之前被await调用因此你在函数内完成的所有初始化打开连接、启动定时任务等都保证发生在服务器开始监听端口之前上下文对象{ app, server }正是ServerSetupFnContext类型的运行时实例——你在setupFn中拿到的app就是 Wasp 构造的 Express 实例server是包装它的http.Server模板中的{# setupFn.isDefined }条件块表明只有在main.wasp里真正声明了setupFn时这段调用代码才会出现在生成产物中。2.3 在 setupFn 中保存供后续使用的值模块级单例模式如果你希望在 Operationsquery/action中使用某些在启动时创建的资源例如某个重型客户端连接推荐的模式是在setupFn中初始化资源并保存在模块作用域然后导出读取函数供 Queries/Actions 直接导入使用。这样该模块就变成了一个在服务启动时完成构造的单例。一个完整的示意例子JavaScript 版// src/myServerSetupCode.js let someResource undefined export const mySetupFunction async () { // 假设 setUpSomeResource 与 startSomeCronJob // 在下方实现或从别的文件导入。 someResource await setUpSomeResource() startSomeCronJob() } export const getSomeResource () someResource// src/queries.js import { getSomeResource } from ./myServerSetupCode.js // ... export const someQuery async (args, context) { const someResource getSomeResource() return queryDataFromSomeResource(args, someResource) }TypeScript 版结构相同只是给函数补上类型标注mySetupFunction: ServerSetupFnQuery 使用wasp/server/operations导出的SomeQuery类型// src/myServerSetupCode.ts import { type ServerSetupFn } from wasp/server let someResource undefined export const mySetupFunction: ServerSetupFn async () { someResource await setUpSomeResource() startSomeCronJob() } export const getSomeResource () someResource// src/queries.ts import { 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) }官方文档的 note 对此模式给出了明确建议把变量放在与 setup 函数相同的模块里再暴露额外的读取函数由 Operations 直接导入使用。Operations 本身的机制见 Operations 文档。三、middlewareConfigFn全局中间件配置函数middlewareConfigFn声明一个 Express 中间件配置函数用于全局性地修改所有 Operationsquery/action与 API 路由的中间件栈。例如为 CORS 增加允许来源、全局追加认证或日志中间件、调整 body 解析行为等。3.1 Wasp 的默认全局中间件栈从 Wasp 生成器的中间件模板 globalMiddleware.ts 可以看到每个 Wasp 应用的 Express 服务器默认携带如下中间件以Map维护键名即身份// This is the set of middleware Wasp supplies by default. const defaultGlobalMiddlewareConfig: MiddlewareConfig new Map([ [helmet, helmet()], // 安全响应头 [cors, cors({ origin: config.allowedCORSOrigins })], // CORS允许来源来自应用配置 [logger, logger(dev)], // morgan 请求日志 [express.json, express.json()], // JSON body 解析Operations 依赖它 [express.urlencoded, express.urlencoded()], // urlencoded body 解析 [cookieParser, cookieParser()] // 解析 req.cookies ])其中express.json对 Operations 的正常工作是必需的服务端通过req.body反序列化入参见 operations.ts 中deserialize(req.body)的用法cors对前后端通信是必需的这些细节也记载于 Configuring Middleware 文档。3.2 用户配置函数的接入机制模板中展示了middlewareConfigFn被接入的方式// 若用户声明了 middlewareConfigFn则导入用户的函数 // 否则退化为恒等函数 const myMiddlewareConfigFn (mc: MiddlewareConfig) mc// 用户函数作用在默认中间件 Map 上得到最终的全局中间件配置。 // 该配置是所有 Operations 与 API 路由的基础除非它们再做各自的定制。 const globalMiddlewareConfig: MiddlewareConfig myMiddlewareConfigFn(defaultGlobalMiddlewareConfig)也就是说middlewareConfigFn接收默认中间件Map作为参数返回修改后的Map。模板中还暴露了globalMiddlewareConfigForExpress函数它先克隆全局中间件 Map再应用传入的每路由定制函数——克隆的目的是避免某个路由的定制污染其他路由看到的全局配置。这解释了为什么文档强调全局修改影响所有 operations 和 APIs并要求你对全局中间件的改动格外谨慎。模板中// NOTE: Remember to update the docs of these change.这行注释也说明默认中间件清单与文档是成对维护的。完整的三级定制体系global / per-api / per-path见 Configuring Middleware 文档。3.3 API 参考app.server 字段一览综合上文app.server的字段定义如下继承自原文档 API Reference// main.wasp app MyApp { title: My app, // ... server: { setupFn: import { mySetupFunction } from src/myServerSetupCode.js, middlewareConfigFn: import { myMiddlewareConfigFn } from src/myServerSetupCode.js } }setupFn: ExtImport—— 声明一个在服务启动时执行的 JavaScript/TypeScript 函数。函数必须是 async 的且会在服务器开始接受任何请求之前被 await 完成。允许在其中做任何自定义初始化例如建立额外的数据库/WebSocket 连接、启动 cron/定时任务。函数上下文接收express.Application与http.Server两个实例便于挂接自定义服务器逻辑。类型签名为export type ServerSetupFn (context: ServerSetupFnContext) Promisevoid export type ServerSetupFnContext { app: Application // express.Application server: Server // http.Server }middlewareConfigFn: ExtImport—— 导入一个 Express 中间件配置函数的语句。这是一项全局性修改影响所有 operations 与 APIs详细用法见 Configuring Middleware 文档。四、关键源码与文档索引内容路径本文档0.13 版 Server Configweb/versioned_docs/version-0.13/project/server-config.mdServer 配置的数据模型Haskellwaspc/src/Wasp/AppSpec/App/Server.hs服务端启动入口模板setupFn 的 await 时机waspc/data/Generator/templates/server/src/server.ts全局中间件模板默认栈与配置函数接入waspc/data/Generator/templates/server/src/middleware/globalMiddleware.tsOperations 请求处理模板依赖 express.jsonwaspc/data/Generator/templates/server/src/middleware/operations.tsServerSetupFn/ServerSetupFnContext类型定义waspc/data/Generator/templates/sdk/wasp/server/types/index.ts中间件配置文档web/versioned_docs/version-0.13/advanced/middleware-config.mdOperations 概览文档web/versioned_docs/version-0.13/data-model/operations/overview.md五、小结app.server是 Wasp 中定制 Node.js 服务端的唯一入口当前包含setupFn与middlewareConfigFn两个可选的ExtImport字段setupFn在server.listen之前被 await接收{ app, server }上下文适合注册自定义路由、初始化外部资源与定时任务配合模块级变量 读取函数的模式可在 Operations 中安全共享这些资源middlewareConfigFn作用在默认中间件Maphelmet、cors、morgan、express.json、express.urlencoded、cookieParser之上其修改对所有 Operations 与 API 路由生效因此应谨慎使用路由级定制应使用 per-api / per-path 机制以上结论均可在 waspc 编译器源码与生成器模板中找到对应实现便于读者在生成产物中自行验证。【免费下载链接】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),仅供参考