ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Mastra Fastify 服务端适配器实战指南:用 `@mastra/fastify` 将 Agent 应用跑在 Fastify 之上

Mastra Fastify 服务端适配器实战指南:用 `@mastra/fastify` 将 Agent 应用跑在 Fastify 之上 Mastra Fastify 服务端适配器实战指南用mastra/fastify将 Agent 应用跑在 Fastify 之上【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastraMastra 是一个用 TypeScript 构建 AI 应用与 Agent 的现代框架而mastra/fastify是它的 Fastify 服务端适配器用于把 Mastra 实例包含 Agent、Workflow、工具、存储、记忆等能力以 HTTP 服务的形式暴露给前端与第三方客户端。本文将以 server-adapters/fastify/README.md 为核心骨架结合该包源码server-adapters/fastify/src/与测试用例完整讲解安装接入、初始化流程、构造参数、路由注册、流式响应、MCP 传输、认证鉴权、文件上传与日志等关键机制让你读完即可把 Mastra 应用无缝嵌入 Fastify 生态。安装mastra/fastify作为独立的 npm 包发布直接安装即可npm install mastra/fastify从包配置server-adapters/fastify/package.json可以看到它的运行前提对等依赖fastify ^5.0.0、mastra/core 1.50.0-0 2.0.0-01.6.0 之后的版本才支持完整 Auth 特性Node.js 运行环境要求22.13.0内部依赖fastify/busboy用于 multipart 表单与文件上传解析mastra/server提供与框架无关的服务端适配层。快速开始三行代码接入 FastifyREADME 给出了最小可运行示例核心思路是先创建 Fastify 实例再把 Mastra 实例与 Fastify 实例一并交给MastraServer调用server.init()注册全部路由后交给 Fastify 监听端口。import Fastify from fastify; import { MastraServer } from mastra/fastify; import { mastra } from ./mastra; const app Fastify({ logger: true }); const server new MastraServer({ app, mastra }); await server.init(); app.listen({ port: 3000 }, (err, address) { if (err) { console.error(err); process.exit(1); } console.log(Server running on ${address}); });这段代码背后的实际行为见 src/index.ts 与基类 packages/server/src/server/server-adapter/index.ts包括自动挂载上下文中间件init()会调用registerContextMiddleware()注册preHandler钩子把requestContext、mastra、registeredTools、taskStore、abortSignal等对象挂到每个 Fastify 请求上自动注册 Mastra 内置路由Agent 对话、Workflow 执行、工具调用、MCP 服务、OpenAPI 文档等路由全部由init()统一注册构造时自动回写MastraServer构造函数末尾会调用mastra.setMastraServer(this)使mastra.getServerApp()可以直接拿到 Fastify 实例对应测试 server-app-access.test.ts。MastraServer 构造参数详解mastra/fastify的MastraServer继承自通用适配器基类MastraServerBase全部构造选项定义在基类构造函数中packages/server/src/server/server-adapter/index.ts#L411-L458参数类型默认值作用appFastifyInstance必填要注入路由的 Fastify 实例mastraMastra必填已配置好的 Mastra 应用实例prefixstring/api所有内置路由的全局前缀会自动规范化openapiPathstring挂载 OpenAPI 文档的路由路径如/openapi.jsonbodyLimitOptions{ maxSize?: number }无请求体/上传文件大小上限字节toolsToolsInput无额外注册到请求上下文中的工具集taskStoreInMemoryTaskStore无用于 A2A/Task 相关能力的存储customRouteAuthConfigMapstring, boolean无自定义路由的按路由鉴权配置streamOptions{ redact?: boolean }{ redact: true }流式输出时是否对敏感数据系统提示词、工具定义、API Key做脱敏customApiRoutesApiRoute[]无自定义 API 路由也可通过new Mastra({ server: { apiRoutes } })配置mcpOptionsMCPOptions无应用于全部 MCP HTTP/SSE 路由的传输选项单条路由可用mcpOptions覆盖其中prefix的默认值值得注意README 的最小示例没有传prefix则内置路由统一挂在/api下示例工程examples/index.ts则显式传入了openapiPath: /openapi.json并额外用 Fastify 原生语法注册了一个/health健康检查路由——说明适配器不会限制你同时使用原生 Fastify 路由能力。完整示例一个带天气 Agent 的 Fastify 服务仓库自带的示例 examples/index.ts 展示了真实的生产用法——把 Agent、工具、Workflow、Memory、存储、可观测性全部串起来const storage new LibSQLStore({ id: fastify-storage, url: file:./mastra.db }); // 1. 定义工具实时天气查询 export const weatherTool createTool({ id: get-weather, description: Get current weather for a location, inputSchema: z.object({ location: z.string().describe(City name) }), outputSchema: z.object({ /* temperature / feelsLike / humidity / ... */ }), execute: async inputData { /* 调用 Open-Meteo API 并返回结构化结果 */ }, }); // 2. 定义带记忆的天气 Agent export const weatherAgent new Agent({ id: weatherAgent, instructions: ..., model: openai(gpt-4o), tools: { weatherTool }, memory: new Memory({ storage, options: { lastMessages: 10 } }), }); // 3. 定义 Workflow先取天气预报再规划活动 const weatherWorkflow createWorkflow({ steps: [fetchWeather, planActivities], ... }) .then(fetchWeather) .then(planActivities); weatherWorkflow.commit(); // 4. 组装 Mastra 实例 const mastra new Mastra({ agents: { newAgent, weatherAgent, planningAgent }, workflows: { weatherWorkflow }, tools: { weatherTool }, storage, observability: new Observability({ default: { enabled: true } }), }); // 5. 挂到 Fastify 上 const app Fastify({ logger: true }); const fastifyServerAdapter new MastraServer({ mastra, app, openapiPath: /openapi.json }); await fastifyServerAdapter.init(); app.get(/health, async () ({ status: ok })); // 原生路由照常可用 app.listen({ port: 3001 }, (err, address) { console.info(Server is running on ${address}); console.info(OpenAPI spec: http://localhost:3001/openapi.json); });示例中还演示了planActivities步骤如何在 Workflow 执行中通过mastra.getAgent(planningAgent)调用另一个 Agent 做流式生成agent.stream(...)逐块消费textStream这体现了适配器对 Agent 流式能力的完整支持。请求上下文与中间件机制上下文中间件如何注入 RequestContextcreateContextMiddleware()src/index.ts返回一个 FastifypreHandler钩子其核心职责POST/PUT 请求从application/json请求体的requestContext字段读取上下文GET 请求从查询参数requestContext读取先尝试 JSON 解析失败则尝试 Base64 解码后再 JSON 解析元数据注入把mastra、registeredTools、taskStore、customRouteAuthConfig挂到请求对象并应用请求头元数据如 User-Agent、来源 IP 等AbortSignal 管理为每个请求创建AbortController仅在请求真正中断客户端断开且响应未正常完成时触发abort()避免正常完成请求被误取消有专门测试覆盖见 fastify-adapter.test.ts 的 Abort Signal 小节。为让 Fastify 行为与 Express 对齐registerContextMiddleware()还做了两件关键改造见 src/index.ts#L906-L938JSON 解析器覆盖允许Content-Type: application/json的空请求体返回undefined而非报错multipart 解析器占位注册multipart/form-data解析器但不实际解析交由getParams中用 busboy 手动处理从而支持文件上传。路由处理链路从请求到响应的完整管线每个注册的路由 handlerregisterRoutesrc/index.ts#L528-L768遵循统一的处理顺序按路由鉴权checkRouteAuth处理登录态校验与透明会话刷新失败时会把Set-Cookie等刷新头带上参数解析getParams合并 URL 路径参数、查询参数重复参数会归一化见normalizeQueryParams、请求体multipart 由 busboy 解析文件转为 BufferJSON 字符串字段自动尝试反序列化Schema 校验分别对 query、body、path 参数执行 Zod 校验校验失败返回 400并把 ZodError 细节透出给客户端RBAC 权限校验当 Mastra 配置了 Studio 或 Server 的 auth 时按惯例从路由路径/方法自动推导所需权限依赖mastra/core 1.6.0见loadHasPermissionFGA 授权校验调用checkRouteFGA做基于 Fine-Grained Authorization 的授权执行 handler 并发送响应sendResponse按responseType分发到 JSON / stream / datastream / MCP 传输等不同通道统一错误处理HTTPException与带status的MastraError会映射为对应 HTTP 状态码其余异常记录日志并返回 500。对ALL方法与路由冲突的处理Fastify 不像 Express 那样原生支持ALL方法因此适配器在注册method ALL的路由时会拆分成GET / POST / PUT / DELETE / PATCH五个方法分别注册并跳过 HEAD/OPTIONS 以避免与 Fastify 自动生成的预检路由冲突对重复注册抛出的 already declared 错误会静默跳过registerRoute与registerCustomApiRoutes中均有此逻辑。流式响应SSE 与 AI SDK DataStream流式输出是 AI 服务最核心的能力之一mastra/fastify的stream()方法src/index.ts#L159-L269对其做了细致处理SSE 格式Content-Type: text/event-streamCache-Control: no-cacheConnection: keep-alive配合X-Accel-Buffering: no以绕过 Nginx 等反向代理的缓冲每个数据块写为data: {json}\n\n默认流格式text/plain chunked 传输块之间用\x1ERecord Separator分隔便于客户端按帧解析SSE 注释透传以:开头的注释块如心跳: heartbeat\n\n原样写出不套data:包装测试 fastify-adapter.test.ts 验证了这一点连接即刷屏路由设置sseFlushOnConnect: true时建立连接后立即写入: connected\n\n注释帮助代理层与浏览器尽快感知连接可用敏感数据脱敏默认开启streamOptions.redact true通过redactStreamChunk剔除系统提示词、工具定义、API Key 等测试用SECRET_SYSTEM_PROMPT、secret_tool等标记验证了脱敏与redact: false时的原样透出序列化容错serializeStreamChunk对 BigInt 等可安全序列化的值做转换对完全无法序列化的块记录日志并跳过——单块失败不会终止整个 HTTP 流该行为对应Stream Chunk Serialization测试防止了此前 JSON.stringify 抛错导致流被静默关闭的缺陷取消传播客户端断开时通过reader.cancel()停止上游读取避免资源泄漏。另外sendResponse还专门处理了datastream-response类型当 handler 返回 AI SDK 的Response对象时会把其 headers、status 与 body 完整透传给 Fastify 响应datastream-error-handling.test.ts覆盖了此路径的异常处理。MCP 传输HTTP 与 SSE 两种模式Mastra 内置 MCPModel Context Protocol能力适配器通过sendResponse的mcp-http与mcp-sse分支透传MCP Streamable HTTP调用server.startHTTP()把 Fastify 已解析的 body 回填到原始请求对象因为 Fastify 已消费 body 流再交给 MCP 服务端处理MCP SSE调用server.startSSE()建立 SSE 长连接ssePath与messagePath均拼接全局prefix两种模式都通过reply.hijack()接管 Fastify 的响应控制权直接写reply.raw出错时若响应头尚未发出则回写标准的jsonrpc错误帧code-32603。相关路由的端到端行为由 mcp-routes.test.ts 与 mcp-transport.test.ts 覆盖。认证与鉴权中间件、RBAC 与 FGA适配器导出了独立的createAuthMiddlewareauth-middleware.ts可像普通 Fastify 中间件一样用于保护特定路由import { createAuthMiddleware } from mastra/fastify; app.addHook(preHandler, createAuthMiddleware({ mastra, requiresAuth: true }));其行为要点requiresAuth: false时直接放行未配置 server auth 时也直接放行Token 来源优先Authorization: Bearer token其次查询参数?apiKey...内部调用coreAuthMiddleware完成 token 校验并把当前路由标记为已鉴权从而与适配器内置的按路由鉴权体系协作。在内置路由层面registerRoute与registerCustomApiRoutes都会执行三层检查路由级鉴权登录态→RBAC 权限按路由路径/方法自动推导所需权限mastra__userPermissions来自请求上下文→FGA 细粒度授权checkRouteFGA基于 URL/query/body 参数做属性化授权。认证失败时返回 401 并携带会话刷新头。RBAC 依赖mastra/core 1.6.0缺失时会有明确的升级提示对应 rbac-permissions.test.ts 与 auth-middleware.test.ts。文件上传、请求体限制与 HTTP 日志multipart 文件上传getParams中通过fastify/busboy解析multipart/form-datasrc/index.ts#L312-L367文件字段聚合成Buffer暴露给 handler普通字段尝试 JSON.parse解析失败则保留原始字符串如options这类 JSON 字符串字段超限文件触发fileSize限制并抛错File size limit exceeded测试验证了文件内容可完整还原以及超过bodyLimitOptions.maxSize时返回 4xx 而不是挂起。请求体大小限制适配器在注册路由时把route.maxBodySize ?? this.bodyLimitOptions?.maxSize传给 Fastify 的 route 级bodyLimit选项并注册专门错误处理器捕获FST_ERR_CTP_BODY_TOO_LARGE返回 413{ error: Request body too large }其他错误继续上抛src/index.ts#L718-L767。注意这里特意不用 Fastify 的config字段因为它是任意元数据Fastify 的 body 解析管线根本不会读取它。HTTP 请求日志registerHttpLoggingMiddleware()src/index.ts#L945-L991由mastra.getServer().build.apiReqLogs驱动支持记录method、path、status、durationincludeQueryParams携带查询参数includeHeaders携带请求头并对redactHeaders列表中指定的头做[REDACTED]替换excludePaths排除特定路径level自定义日志级别。对应测试见 http-logging.test.ts。自定义 API 路由与 OpenAPIcustomApiRoutes或new Mastra({ server: { apiRoutes: [...] } })允许注册自定义业务路由适配器会为它们复用完整的鉴权/RBAC/FGA 管线registerCustomApiRoutessrc/index.ts#L770-L904。要点支持createRoute定义的带 Schema 校验路由bodySchema 校验失败返回 400以及registerApiRoute定义的 Fetch-like 处理器返回Response自定义路由路径不得与全局 prefix 冲突——若以 prefix 开头会在init()时直接抛错见 Custom route prefix validation 测试返回的Response会与 Fastify 钩子/插件如fastify/cors设置的响应头合并set-cookie采用追加语义保证插件 Cookie 与处理器 Cookie 共存。设置openapiPath后适配器会自动生成并挂载 OpenAPI 文档路由基类registerOpenAPIRoutepackages/server/src/server/server-adapter/index.ts#L1284便于对接 Swagger UI 或 API 网关。关于插件响应头在流式响应中的保留fastify-adapter.test.ts 的 Plugin Headers on Stream Responses 小节验证了即使流式路由调用了reply.hijack()钩子设置的access-control-allow-origin等头仍会出现在最终响应中。小结mastra/fastify是一个与框架深度集成的服务端适配器它基于mastra/server的通用适配层实现把 Mastra 的 Agent、Workflow、工具、MCP、OpenAPI 能力以标准的 Fastify 路由暴露出来同时保留了 Fastify 的中间件、钩子、插件生态如 CORS、Swagger与原生路由能力。从源码看它在流式输出、请求取消、multipart 上传、请求体限制、认证鉴权Auth/RBAC/FGA、敏感数据脱敏与 HTTP 日志等工程细节上都做了专门处理并有成体系的测试保障src/tests/ 下共 13 个测试文件覆盖各条链路。如果你正在用 Fastify 构建 Node 服务且需要快速接入 AI 能力直接在mastra.init()之前将MastraServer挂到现有FastifyInstance上即可二者可以无缝共存。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表