tRPC × AWS Lambda Adapter 实战指南:接入 API Gateway v1/v2、Lambda Function URL 与响应流式返回
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
导读
本文以 tRPC 官方适配器@trpc/server/adapters/aws-lambda为主线,系统讲解如何将 tRPC Router 部署到 AWS Lambda,覆盖 API Gateway REST API(v1 载荷格式)、HTTP API(v2 载荷格式)与 Lambda Function URL 三种接入方式,并深入剖析awsLambdaStreamingRequestHandler+awslambda.streamifyResponse()的响应流式(response streaming)用法、maxBatchSize批量限制以及高频踩坑点。读完本文,你将掌握从上下文注入、载荷格式区分到客户端httpBatchLink/httpLink正确选型的完整落地能力。
本文对应仓库中的技能文档 packages/server/skills/adapter-aws-lambda/SKILL.md,其内容主体源自官方适配器说明 www/docs/server/adapters/aws-lambda.md,并配有 examples/lambda-api-gateway、examples/lambda-api-gateway-streaming 与 examples/lambda-url 三个可运行示例。
适配器支持矩阵与原理
AWS Lambda 适配器并非把 tRPC 当作独立 HTTP 服务监听端口,而是导出一个由 API Gateway / Function URL 直接调用的 Lambda handler。请求事件(event)与运行时上下文(context)由 AWS 注入,适配器负责完成「网关事件 → tRPC HTTP 请求」的解包,以及「tRPC 响应 → 网关响应」的封包。
它支持的三种接入场景:
| 接入方式 | 载荷格式 | 对应事件类型 |
|---|---|---|
| API Gateway REST API | v1.0 | APIGatewayProxyEvent |
| API Gateway HTTP API | v2.0(可配置为 1.0) | APIGatewayProxyEventV2 |
| Lambda Function URL | — | APIGatewayProxyEventV2 |
对应源码位于 packages/server/src/adapters/aws-lambda,对外暴露两个核心入口:
awsLambdaRequestHandler:标准(缓冲式)请求处理器;awsLambdaStreamingRequestHandler:流式响应处理器,需配合awslambda.streamifyResponse()使用。
最小可运行示例
以下完整代码是官方技能文档中的标准起步模板,可直接保存为server.ts:
import { initTRPC } from '@trpc/server'; import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda'; import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda'; import type { APIGatewayProxyEventV2 } from 'aws-lambda'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ greet: t.procedure .input(z.object({ name: z.string() })) .query(({ input }) => ({ greeting: `Hello, ${input.name}!` })), }); export type AppRouter = typeof appRouter; const createContext = ({ event, context, }: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({ event, lambdaContext: context, }); export const handler = awsLambdaRequestHandler({ router: appRouter, createContext, });部署完成后,通过网关 URL 即可直接调用。以官方适配器文档为例,若网关 event 暴露了查询 proceduregetUser,调用形如:
GET https://<execution-api-link>/getUser?input=INPUT其中INPUT是 URI 编码的 JSON 字符串。也就是说,tRPC 在 API Gateway 场景下把 procedure 映射为 URL 路径,输入参数通过input查询参数传递。
createContext:把网关事件注入 tRPC 上下文
createContext每个请求执行一次,签名由CreateAWSLambdaContextOptions<T>泛型参数决定。它解构出两个关键字段:
event:API Gateway / Function URL 传入的原始事件对象,包含请求头、路径、authorizer 等;context:Lambda 运行时上下文(函数名、内存、剩余时间等)。
你可以把鉴权信息塞进上下文,例如从 API Gateway 的 authorizer claims 中读取用户:
import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda'; import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda'; import type { APIGatewayProxyEvent } from 'aws-lambda'; import { appRouter } from './router'; const createContext = ({ event, context, }: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>) => ({ user: event.requestContext.authorizer?.claims, }); export const handler = awsLambdaRequestHandler({ router: appRouter, createContext, });仓库示例 examples/lambda-api-gateway/src/server.ts 展示了更贴近生产的一种上下文构造——把event、通过(event as { version?: string }).version判定的载荷版本,以及请求头中的x-user一并注入:
function createContext({ event, context, }: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>) { return { event: event, apiVersion: (event as { version?: string }).version ?? '1.0', user: event.headers['x-user'], }; } type Context = Awaited<ReturnType<typeof createContext>>; const t = initTRPC.context<Context>().create();注意这里使用了initTRPC.context<Context>().create(),将上下文类型显式接入 router,随后 procedure 内即可通过opts.ctx.user读取请求头携带的用户标识。
区分载荷格式版本 v1.0 与 v2.0
这是 AWS Lambda 适配器最容易被忽视的细节。API Gateway 调用 Lambda 时存在两种事件数据格式:
- 版本 1.0→ 事件类型
APIGatewayProxyEvent,用于 REST API; - 版本 2.0→ 事件类型
APIGatewayProxyEventV2,用于 HTTP API(HTTP API 也允许显式配置为 1.0)。
tRPC 的两个处理函数均同时支持两种事件类型,关键在于给CreateAWSLambdaContextOptions<T>传对泛型。官方文档给出如下类型推导写法,便于在 createContext 中确定当前网关使用的版本:
import type { APIGatewayProxyEvent } from 'aws-lambda'; import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda'; function createContext({ event, context, }: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>) { // ... } // CreateAWSLambdaContextOptions<APIGatewayProxyEvent> 或 CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>仓库中 examples/lambda-api-gateway/src/payloadFormatVersionClient.ts 展示了「一个 handler 同时服务 v1 REST API 与 v2 HTTP API」的经典拓扑:由serverless.yml声明两个函数事件,分别指向httpApi: '*'与http: { path: /{proxy+}, method: any },再用两个 client(不同 base URL)分别验证两种载荷格式都能被正确处理。
Lambda Function URL 场景
若不希望引入 API Gateway,可以直接给 Lambda 绑定 Function URL。Function URL 使用 v2 载荷格式事件,因此上下文泛型应使用APIGatewayProxyEventV2。仓库示例 examples/lambda-url/src/server.ts 同时演示了普通 procedure、异步生成器流式 procedure 与延迟返回 procedure 三种形态,可作为 Function URL 场景的完整参照。
响应流式返回(Response Streaming)
流式 handler 的签名差异
awsLambdaStreamingRequestHandler的 handler 签名与默认 handler 不同:除了常规的event与context参数,流式 handler 内部还额外接收一个可写流参数responseStream。为了让 Lambda 按流式而非缓冲方式处理响应,必须用awslambda.streamifyResponse()装饰整个 handler:
/// <reference types="aws-lambda" /> import type { CreateAWSLambdaContextOptions } from '@trpc/server/adapters/aws-lambda'; import { awsLambdaStreamingRequestHandler } from '@trpc/server/adapters/aws-lambda'; import type { APIGatewayProxyEventV2 } from 'aws-lambda'; import { appRouter } from './router'; const createContext = ({ event, context, }: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({}); export const handler = awslambda.streamifyResponse( awsLambdaStreamingRequestHandler({ router: appRouter, createContext, }), );awslambda命名空间由 Lambda 执行环境自动提供,无需也不应从 npm 安装;若要获得类型提示,可通过@types/aws-lambda中的全局类型增强,配合文件顶部的/// <reference types="aws-lambda" />指令引用。
响应流式目前支持的接入面:Lambda Function URLs 与 API Gateway REST APIs。对 REST API 集成还需在网关侧把集成配置为responseTransferMode: STREAM。
定义一个可流式的 async generator procedure
普通 query 返回的是完整结果,而流式返回依赖async generatorprocedure——每yield一次,客户端就能收到一段增量数据:
import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); export const appRouter = t.router({ countdown: t.procedure.query(async function* () { for (let i = 10; i >= 0; i--) { await new Promise((resolve) => setTimeout(resolve, 500)); yield i; } }), });仓库中的流式示例 examples/lambda-api-gateway-streaming/src/server.ts 更进一步,在同一 router 内混合了三种 procedure:
greet:普通 query;iterable:async generator,每 500ms 产出一个递增数字;deferred:根据输入wait参数人为延迟后返回。
这说明流式 handler 与普通 handler 共用同一套 router,单个函数可以同时承担「即时响应」与「流式响应」两种能力。
客户端如何消费流
服务端开启流式之后,客户端需要使用支持流式读取的 link。技能文档明确指出:与httpBatchStreamLink配对使用,即客户端用httpBatchStreamLink而非httpBatchLink发起请求,才能把流式响应逐段呈现给上层。
用 maxBatchSize 限制请求批量大小
tRPC 客户端会把同批次内的多个调用合并成一个 HTTP 请求(如路径形如getUser,createUser),服务端默认接受批量请求。若网关路径或业务对单次批量数量敏感,可显式限制:
import { awsLambdaRequestHandler } from '@trpc/server/adapters/aws-lambda'; import { appRouter } from './router'; export const handler = awsLambdaRequestHandler({ router: appRouter, createContext, maxBatchSize: 10, });- 单请求携带超过
maxBatchSize个操作时,服务端以400 Bad Request拒绝; - 为避免无谓报错,客户端
httpBatchLink/httpBatchStreamLink上的maxItems应设置为与服务端相同的数值。
高频踩坑(Common Mistakes)
坑 1(高危):在「每 procedure 一个 API Gateway 资源」的架构里误用 httpBatchLink
// 错误示范 // API Gateway 为每个 procedure 单独建资源,例如 /getUser、/createUser import { httpBatchLink } from '@trpc/client'; httpBatchLink({ url: 'https://api.example.com' }); // 批量请求打到 /getUser,createUser → 404httpBatchLink会把多个 procedure 名用逗号拼进 URL 路径。当网关按 procedure 逐一配置资源时,getUser,createUser这种合并路径匹配不到任何资源,直接 404。正确做法二选一:
import { httpBatchLink, httpLink } from '@trpc/client'; // 方案 A:网关只建一个 catch-all 资源(如 /{proxy+}),可继续使用批量 httpBatchLink({ url: 'https://api.example.com' }); // 方案 B:每 procedure 一个资源的场景改用 httpLink(放弃批量) httpLink({ url: 'https://api.example.com' });仓库 examples/lambda-api-gateway/serverless.yml 就是方案 A 的标准配置:HTTP API 使用events: - httpApi: '*',REST API 使用path: /{proxy+}配合method: any,保证任意路径都能路由到同一个 handler,从而让httpBatchLink正常工作。
坑 2(高危):流式 handler 漏包 streamifyResponse
// 错误示范 export const handler = awsLambdaStreamingRequestHandler({ router: appRouter, createContext, });// 正确示范 export const handler = awslambda.streamifyResponse( awsLambdaStreamingRequestHandler({ router: appRouter, createContext, }), );awsLambdaStreamingRequestHandler必须被awslambda.streamifyResponse()包裹才能真正开启 Lambda 响应流式模式;否则 Lambda 会把它当作标准缓冲式 handler 处理,响应将被整体缓冲后再返回,流式语义失效。同理,也不要画蛇添足地对标准awsLambdaRequestHandler使用streamifyResponse。
完整落地路线图
- 安装依赖:在 Lambda 函数项目中加入
@trpc/server(以及配套校验库如zod);客户端侧安装@trpc/client。注意运行时版本需与所用 API Gateway / Lambda 配置兼容。 - 定义 Router:用
initTRPC.create()(或initTRPC.context<Context>().create())声明 procedures,并export type AppRouter = typeof appRouter供客户端复用类型。 - 编写 createContext:以
CreateAWSLambdaContextOptions<APIGatewayProxyEvent>或<APIGatewayProxyEventV2>约束签名,把event、context、鉴权 claims、自定义头等放入上下文。 - 导出 handler:选择
awsLambdaRequestHandler(普通)或awslambda.streamifyResponse(awsLambdaStreamingRequestHandler({...}))(流式)。 - 配置网关:REST API 使用
/下的{proxy+}兜底路由并转发所有方法与任意路径;HTTP API 使用通配路由;REST API 流式需开启responseTransferMode: STREAM。 - 客户端接入:单资源 catch-all 路由可用
httpBatchLink;需要消费流式 async generator 时改用httpBatchStreamLink;每 procedure 独立资源则退化为httpLink,并让maxItems与服务端maxBatchSize保持一致。 - 本地联调:可参考 examples/lambda-api-gateway/package.json 引入
serverless-esbuild与serverless-offline,在本地模拟网关事件、用 examples/lambda-api-gateway/src/client.ts 验证链路后再部署到云端。
关联技能与延伸阅读
本文所属的技能体系位于 packages/server/skills/adapter-aws-lambda,编排文件 packages/server/skills/adapter-aws-lambda/SKILL.md 中还标注了以下前置与相邻技能,可按需串联学习:
- server-setup:
initTRPC.create()、router/procedure 定义与 context 基础(server-setup 技能); - adapter-fetch:面向 edge/serverless 运行时、基于 Fetch API 的替代适配方案(adapter-fetch 技能);
- links:
httpBatchLink与httpLink在 API Gateway 路由约束下的取舍(客户端 links 目录)。
更完整的适配器背景知识(载荷格式版本、部署后的调用 URI 表、示例应用索引)可查阅官方文档 www/docs/server/adapters/aws-lambda.md,三个贴近生产的可运行示例分别在 examples/lambda-api-gateway、examples/lambda-api-gateway-streaming 与 examples/lambda-url。
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考