ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

tRPC Fastify Adapter 实战指南:从 HTTP 路由到 WebSocket 实时订阅

tRPC Fastify Adapter 实战指南:从 HTTP 路由到 WebSocket 实时订阅 tRPC Fastify Adapter 实战指南从 HTTP 路由到 WebSocket 实时订阅【免费下载链接】trpc‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc本文以 tRPC v11 的 Fastify 适配器为核心完整讲解如何将 tRPC Router 挂载为 Fastify 插件、配置上下文Context、暴露 HTTP 端点并逐步启用基于 WebSocket 的实时订阅能力。读完你将掌握trpc/server/adapters/fastify的全部安装步骤、核心配置项与底层调用机制并能照此在自己的 Fastify v5 服务中落地端到端类型安全的 API。一、适配器能做什么把 tRPC Router 变成 Fastify 插件tRPC 为 Fastify 提供了开箱即用的适配器。它的核心思路是将你定义的 tRPC Router 包装成一个 Fastify 插件Fastify Plugin通过server.register(...)挂载到任意 Fastify 实例上从而同时获得标准的 HTTP JSON 端点query / mutation沿用 Fastify 的路由前缀机制基于 WebSocket 的 subscription 实时通道借助fastify/websocket。从源码看适配器由两部分构成见 packages/server/src/adapters/fastify文件职责fastifyTRPCPlugin.ts导出 Fastify 插件fastifyTRPCPlugin、插件选项类型与CreateFastifyContextOptions负责注册内容解析器、挂载 HTTP 路由与 WebSocket 握手路由fastifyRequestHandler.ts将 Fastify 的req/res桥接为 tRPC 内部 HTTP 处理器逐个调用并回写响应fastifyRequestHandler会把 Fastify 请求体的内容打补丁到 Node 的IncomingMessage上再交给 tRPC 统一的resolveResponse流程处理见 fastifyRequestHandler.ts因此 tRPC 的批处理、错误格式化、Transformer 等能力在 Fastify 下与其它适配器保持一致。仓库中提供了一个可完整运行的示例工程推荐直接对照阅读examples/fastify-server其中 server.ts 展示了生产可用的启动结构。Fastify 版本要求重要tRPC v11 的 Fastify 适配器要求Fastify v5 及以上版本。若使用 Fastify v4请求可能返回空响应且不报错。示例工程 package.json 中依赖即为fastify^5.0.0。二、从零搭建HTTP 层完整步骤1. 安装依赖在项目中安装trpc/server与fastifyyarn add trpc/server fastify zod说明Zod若你使用 AI 编程代理可先安装 tRPC 相关技能以提升代码生成质量npx tanstack/intentlatest install2. 创建 RouterRouter 是承载 query、mutation、subscription 的核心对象完整概念见 routers。将下面的示例保存为router.tsimport { initTRPC } from trpc/server; import { z } from zod; type User { id: string; name: string; bio?: string; }; const users: Recordstring, User {}; export const t initTRPC.create(); export const appRouter t.router({ getUserById: t.procedure.input(z.string()).query((opts) { return users[opts.input]; // input type is string }), createUser: t.procedure .input( z.object({ name: z.string().min(3), bio: z.string().max(142).optional(), }), ) .mutation((opts) { const id Date.now().toString(); const user: User { id, ...opts.input }; users[user.id] user; return user; }), }); // export type definition of API export type AppRouter typeof appRouter;注意AppRouter类型导出必不可少客户端类型推断与 Fastify 插件选项的类型校验都依赖它。当单个 Router 文件过大时可将其拆分为多个子 Router再通过 merging-routers 合并为根appRouter。3. 创建上下文ContextContext 会在每个请求到来时被创建一次用于注入认证信息、数据库连接等。示例context.tsimport { CreateFastifyContextOptions } from trpc/server/adapters/fastify; export function createContext({ req, res }: CreateFastifyContextOptions) { const user { name: req.headers.username ?? anonymous }; return { req, res, user }; } export type Context AwaitedReturnTypetypeof createContext;从类型定义看CreateFastifyContextOptions实质上是NodeHTTPCreateContextFnOptionsFastifyRequest, FastifyReply见 fastifyTRPCPlugin.ts因此回调能同时拿到 Fastify 的请求与响应对象可直接访问请求头、读取参数等。Context 的详细设计思路参见 context。4. 创建 Fastify 服务并注册插件tRPC 将 Router 转化为 Fastify 插件进行注册。为避免大批量batch请求时报错需要按示例设置 Fastify 的maxParamLength选项import { fastifyTRPCPlugin, FastifyTRPCPluginOptions, } from trpc/server/adapters/fastify; import fastify from fastify; import { createContext } from ./context; import { appRouter, type AppRouter } from ./router; const server fastify({ routerOptions: { maxParamLength: 5000, }, }); server.register(fastifyTRPCPlugin, { prefix: /trpc, trpcOptions: { router: appRouter, createContext, onError({ path, error }) { // report to error monitoring console.error(Error in tRPC handler on path ${path}:, error); }, } satisfies FastifyTRPCPluginOptionsAppRouter[trpcOptions], }); (async () { try { await server.listen({ port: 3000 }); } catch (err) { server.log.error(err); process.exit(1); } })();类型提示Tip受 Fastify 插件系统与类型推断的局限onError等选项有时难以被准确推断类型。可以通过satisfies FastifyTRPCPluginOptionsAppRouter[trpcOptions]显式声明帮助 TypeScript 获得正确类型。从插件源码见 fastifyTRPCPlugin.ts还可以看到插件注册时会做两件关键的事移除并重写application/json内容解析器以parseAs: string方式保留原始字符串体确保 tRPC 能按自己的协议解析入参移除并重写multipart/form-data解析器以支持 非 JSON 内容类型如文件上传。5. 端点一览服务启动后Router 中的 procedure 即可通过 HTTP 访问ProcedureHTTP URIgetUserByIdGET http://localhost:3000/trpc/getUserById?inputINPUT其中INPUT为 URI 编码后的 JSON 字符串createUserPOST http://localhost:3000/trpc/createUserreq.body类型为User也就是说 URL 结构为{prefix}/{procedurePath}prefix默认/trpcprocedurePath即 Router 中的键名。若需要拆分复杂逻辑可参考示例工程将上下文与路由分文件组织见 examples/fastify-server/src/server/router/context.ts 与 server.ts。三、启用 WebSocket为 subscription 打通实时通道Fastify 适配器通过 fastify/websocket。除上文步骤外你还需要安装依赖 → 在 Router 中添加订阅 → 在插件选项中开启useWSS。要求fastify/websocket版本不低于3.11.0示例工程使用的是^11.0.0见 package.json。1. 安装依赖yarn add fastify/websocket2. 注册fastify/websocketimport ws from fastify/websocket; server.register(ws);注意注册顺序fastify/websocket需要先于fastifyTRPCPlugin注册示例 server.ts 即按此顺序执行。3. 在 Router 中添加 subscription编辑上一步创建的router.ts加入订阅import { initTRPC } from trpc/server; const t initTRPC.create(); export const appRouter t.router({ randomNumber: t.procedure.subscription(async function* () { while (true) { yield { randomNumber: Math.random() }; await new Promise((resolve) setTimeout(resolve, 1000)); } }), });这里的subscription使用异步生成器async generator作为实现每 1 秒产出一次随机数正是 tRPC v11 推荐的服务端订阅写法。4. 开启useWSS并配置心跳在注册fastifyTRPCPlugin时传入useWSS: true并可通过trpcOptions.keepAlive配置 WebSocket 心跳import { fastifyTRPCPlugin, FastifyTRPCPluginOptions, } from trpc/server/adapters/fastify; import fastify from fastify; import { createContext } from ./context; import { appRouter, type AppRouter } from ./router; const server fastify(); server.register(fastifyTRPCPlugin, { useWSS: true, trpcOptions: { router: appRouter, createContext, // Enable heartbeat messages to keep connection open (disabled by default) keepAlive: { enabled: true, // server ping message interval in milliseconds pingMs: 30000, // connection is terminated if pong message is not received in this many milliseconds pongWaitMs: 5000, }, }, });完成后客户端即可订阅randomNumber主题每隔约 1 秒收到一个随机数。从实现层面看开启useWSS后插件会额外注册一条GET {prefix ?? /}且{ websocket: true }的路由把握手后的 socket 交给getWSConnectionHandler处理见 fastifyTRPCPlugin.ts当keepAlive.enabled为真时还会调用handleKeepAlive(socket, pingMs, pongWaitMs)服务端按pingMs周期发送 ping若在pongWaitMs内未收到 pong 则终止连接——这有助于穿透中间代理避免空闲连接被断开。相关类型WSSHandlerOptions与心跳机制统一实现在 packages/server/src/adapters/ws.ts。四、Fastify 插件选项速查fastifyTRPCPlugin的完整选项定义见 FastifyTRPCPluginOptions汇总如下nametypeoptionaldefaultdescriptionprefixstringtrue/trpctRPC 路由的 URL 前缀useWSSbooleantruefalse是否通过fastify/websocket启用 WebSocket 支持trpcOptionsFastifyHandlerOptionsAppRouter, Request, Replyfalsen/atRPC 处理器选项包含router、createContext、onError、keepAlive等几点补充prefix的默认值/trpc是 tRPC 层面的语义约定Fastify 注册插件时的封装规则会影响前缀的实际处理插件源码中对通过fastify-plugin二次封装的场景做了兼容处理见 fastifyTRPCPlugin.ts日常使用直接按上述示例传入即可trpcOptions.router接收根appRoutercreateContext若省略则每次请求以空 Context 处理trpcOptions.onError会在过程执行抛错时被调用由 fastifyRequestHandler.ts 转发常用于接入错误监控平台。五、示例工程与延伸阅读建议以仓库内的完整示例作为实战起点examples/fastify-server。该工程采用服务工厂模式封装了创建与启停逻辑并把 Router、子路由与 Context 分目录管理可通过以下命令快速体验cd examples/fastify-server # 先安装依赖再分别启动服务端与一个简单的 Node 客户端 yarn yarn dev需要深入了解的关联主题均在仓库 www/docs 目录下routers 与 merging-routersRouter 的构建与拆分合并context请求上下文的生命周期与典型用法subscriptions 与 websockets服务端订阅与 WebSocket 协议细节adapters-intro各运行环境适配器的横向对比fastifyRequestHandler.ts如需深入适配器桥接原理可从这里读起。【免费下载链接】trpc‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表