
如何用 tRPC lazy 动态加载路由减少 serverless 冷启动开销【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc当 tRPC 项目的 API 拆成多个 router 之后如果入口文件把所有 router 一次性import进来应用每次冷启动都会加载全部模块。官方文档在 Merging Routers 一节的 Dynamically load routers 中给出的方案是用trpc/server导出的lazy函数动态加载 router 模块按需加载后再挂载到应用 router 上。文档原话是You can use thelazyfunction to dynamically load your routers. This can be useful to reduce cold starts of your application. Theres no difference in how you use the router after its been lazy loaded vs. how you use a normal router.也就是说lazy用于把重 router 移出启动路径只在实际被调用时才加载加载完成之后客户端调用方式和类型推导与普通 router 完全一致。lazy 的两种写法routers/_app.ts 中的示例展示了两种写法区别在于模块里导出几个 routerimport { lazy } from trpc/server; import { router } from ../trpc; export const appRouter router({ // Option 1: Short-hand when the module has exactly 1 router exported greeting: lazy(() import(./greeting.js)), // Option 2: if exporting more than 1 router user: lazy(() import(./user.js).then((m) m.userRouter)), }); export type AppRouter typeof appRouter;写法 1 是简写模块只导出一个 router 时直接把import()的 Promise 传给lazy写法 2 用于模块导出多个 router 的情况用.then((m) m.userRouter)从模块里取出目标 router最后一行export type AppRouter typeof appRouter保留不变客户端照样用这个类型做端到端类型推导。lazy的实现位于 packages/server/src/unstable-core-do-not-import/router.ts。官方完整示例examples/lazy-load仓库里的 examples/lazy-load 是一个可运行的最小示例README 注明环境要求Requires node 18 (for global fetch)。示例的 tsconfig.json 使用target: esnext、module: NodeNextpackage.json 声明了type: module所以动态import()的模块内都带.js扩展名。先看入口如何组装 routersrc/server/routers/_app.tsimport { lazy } from trpc/server; import { router } from ../trpc.js; export const appRouter router({ user: lazy(() import(./user.js)), // Alternative way to lazy load slow: lazy(() import(./slow.js)), }); export type AppRouter typeof appRouter;tRPC 初始化在 src/server/trpc.ts每个 router 模块从这里复用router和publicProcedureimport { initTRPC } from trpc/server; const t initTRPC.create(); export const router t.router; export const publicProcedure t.procedure;示例中最能说明“按需加载”的是 src/server/routers/slow.ts——它把日志和一段 3 秒延迟写在模块顶层用来模拟一个加载很慢的模块import { publicProcedure, router } from ../trpc.js; console.log( Lazy loading slow router...); await new Promise((resolve) setTimeout(resolve, 3000)); export const slowRouter router({ hello: publicProcedure.query(() world), });由于slow.js只在lazy(() import(./slow.js))被触发时才执行import()这些顶层日志和延迟不会在应用启动时发生只在第一次加载该模块时发生。src/server/routers/user.ts 是普通 routerlist/byId/create三个 procedure查一个内置的假数据库同样被包在lazy里。服务端入口 src/server/index.ts 用 standalone adapter 在 3000 端口起 HTTP 服务import { createHTTPServer } from trpc/server/adapters/standalone; import { appRouter } from ./routers/_app.js; const server createHTTPServer({ router: appRouter, }); server.listen(3000);客户端 src/client/index.ts 只从服务端导入AppRouter类型调用方式和普通 router 没有任何区别import { createTRPCClient, httpBatchLink } from trpc/client; import type { AppRouter } from ../server/routers/_app.js; const trpc createTRPCClientAppRouter({ links: [ httpBatchLink({ url: http://localhost:3000, }), ], }); const hello await trpc.slow.hello.query(); console.log(slow.hello:, hello);运行与验证在examples/lazy-load目录下按 README 给出的命令执行npm i npm run devdev脚本见 package.json会并行跑dev:servertsx watch src/server和dev:client等待 3000 端口后tsx watch src/client。运行成功时客户端会依次打印查询结果其中slow.hello返回world文档示例输出。观察 lazy 加载是否生效看两点服务启动阶段不会出现slow.ts顶层的 Lazy loading slow router...日志当客户端第一次调用trpc.slow.hello.query()触发模块加载时该日志才出现且这次调用会额外等待模块顶层的 3 秒模拟延迟这是示例刻意写入的真实项目里对应的是慢模块自身的加载成本。如果要验证构建产物示例的build脚本就是tsc。这里有一处文档不一致需要留意README 的 Building 一节写的是npm run buildnpm run start但 package.json 中并没有定义start脚本实际可用的是build、dev、test-devstart-server-and-test tsx src/server 3000 tsx src/client和test-startstart-server-and-test node dist/server 3000 node dist/client。要跑构建后的验证用npm run build后再执行npm run test-start即可。限制与注意点lazy简写只适用于“模块恰好导出 1 个 router”的情况一个模块导出多个 router 时必须用.then((m) m.xxxRouter)显式取出口。文档示例对lazy代码块标注了target: esnext即动态import()需要较新的编译目标官方示例的 tsconfig 是esnextNodeNext且以 ESMtype: module、.js扩展名方式书写。lazy只是把模块加载推迟到首次调用文档明确说的是“useful to reduce cold starts”即它减少的是启动时必须执行的模块router 内部依赖如数据库连接在首次调用时的初始化成本不会因此消失示例中 3 秒延迟就是用来演示这部分成本的。下一步如果要把 API 继续拆细可参考 merging-routers 文档 中 child routers 与t.mergeRouters的两种组织方式再对其中体积较大的模块套用本文的lazy写法。【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考