@trpc/server
Version:
230 lines (171 loc) • 11.7 kB
Markdown
---
name: adapter-aws-lambda
description: Deploy tRPC on AWS Lambda with awsLambdaRequestHandler() from /server/adapters/aws-lambda for API Gateway v1 (REST, APIGatewayProxyEvent) and v2 (HTTP, APIGatewayProxyEventV2), and Lambda Function URLs. Enable response streaming with awsLambdaStreamingRequestHandler() wrapped in awslambda.streamifyResponse(). CreateAWSLambdaContextOptions provides event and context for context creation. Use one {proxy+} resource for httpBatchLink, or httpLink for a resource per procedure.
requires:
- server-setup
sources:
- www/v11/docs/server/adapters/aws-lambda.md
- www/v11/docs/server/adapters.md
metadata:
type: core
library: trpc
library_version: 11.25.0
---
<!-- Generated from www/v11/docs/server/adapters/aws-lambda.md by www/build/skills.ts. Edit the docs instead. -->
# AWS Lambda + API Gateway Adapter
From https://trpc.io/docs/server/adapters/aws-lambda
## AWS Lambda adapter
The AWS Lambda adapter is supported for API Gateway [REST API(v1)](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-rest-api.html) and [HTTP API(v2)](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api.html), and [Lambda Function URL](https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html) use cases.
> `httpBatchLink` requires the router to work on a single API Gateway Resource (as shown in the [example](https://github.com/trpc/trpc/tree/main/examples/lambda-api-gateway)).
> If you'd like to have a Resource per procedure, you can use the `httpLink` instead ([more info](https://github.com/trpc/trpc/issues/5738#issuecomment-2130001522)).
## Example apps
<table>
<thead>
<tr>
<th>Description</th>
<th>Links</th>
</tr>
</thead>
<tbody>
<tr>
<td>API Gateway with NodeJS client.</td>
<td>
<ul>
<li><a href="https://github.com/trpc/trpc/tree/main/examples/lambda-api-gateway">Source</a></li>
</ul>
</td>
</tr>
<tr>
<td>API Gateway REST API with response streaming.</td>
<td>
<ul>
<li><a href="https://github.com/trpc/trpc/tree/main/examples/lambda-api-gateway-streaming">Source</a></li>
</ul>
</td>
</tr>
</tbody>
</table>
## How to add tRPC
### 1. Install deps
```bash
yarn add /server
```
### 2. Create a tRPC router
Implement your tRPC router. A sample router is given below:
```ts no-check title="server.ts"
import { initTRPC } from '/server';
import { z } from 'zod';
export const t = initTRPC.create();
const appRouter = t.router({
getUser: t.procedure.input(z.string()).query((opts) => {
opts.input; // string
return { id: opts.input, name: 'Bilbo' };
}),
});
// export type definition of API
export type AppRouter = typeof appRouter;
```
### 3. Use the Amazon API Gateway adapter
tRPC includes an adapter for API Gateway out of the box. This adapter lets you run your routes through the API Gateway handler.
```ts no-check title="server.ts"
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
import type { CreateAWSLambdaContextOptions } from '/server/adapters/aws-lambda';
import { awsLambdaRequestHandler } from '/server/adapters/aws-lambda';
import { appRouter } from './router';
// created for each request
const createContext = ({
event,
context,
}: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({}); // no context
type Context = Awaited<ReturnType<typeof createContext>>;
export const handler = awsLambdaRequestHandler({
router: appRouter,
createContext,
})
```
`awsLambdaRequestHandler` accepts the [options every adapter takes](https://trpc.io/docs/server/adapters#common-options), like `onError` and `maxBatchSize`.
Build & deploy your code, now use your API Gateway URL to call your function.
| Endpoint | HTTP URI |
| --------- | ------------------------------------------------------------------------------------------------------------ |
| `getUser` | `GET https://<execution-api-link>/getUser?input=INPUT` <br/><br/>where `INPUT` is a URI-encoded JSON string. |
#### A word about payload format version
API Gateway has two different event data formats when it invokes a Lambda. For REST APIs they should be version "1.0"(`APIGatewayProxyEvent`), but you can choose which for HTTP APIs by stating either version "1.0" or "2.0".
- Version 1.0: `APIGatewayProxyEvent`
- Version 2.0: `APIGatewayProxyEventV2`
To infer what version you might have, supply the context as following:
```ts no-check
function createContext({
event,
context,
}: CreateAWSLambdaContextOptions<APIGatewayProxyEvent>) {
// ...
}
// CreateAWSLambdaContextOptions<APIGatewayProxyEvent> or CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>
```
[Read more here about payload format version](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html)
## AWS Lambda Response Streaming Adapter
AWS Lambda supports streaming responses to clients with both Lambda Function URLs and API Gateway REST APIs.
> Response streaming is supported for Lambda Function URLs and API Gateway REST APIs. For API Gateway REST APIs, you need to configure the integration with `responseTransferMode: STREAM`. [Read more about Lambda response streaming](https://aws.amazon.com/blogs/compute/introducing-aws-lambda-response-streaming/) and [API Gateway response streaming](https://aws.amazon.com/blogs/compute/building-responsive-apis-with-amazon-api-gateway-response-streaming/).
### Response Streaming
The signature of a streaming handler is different from the default handler. The streaming handler additionally receives a writable stream parameter, `responseStream`, besides the default node handler parameters, `event` and `context`. To indicate that Lambda should stream your responses, you must wrap your function handler with the `awslambda.streamifyResponse()` decorator.
> Note that the `awslambda` namespace is automatically provided by the Lambda execution environment. You can import the types from `/aws-lambda` to augment the global namespace with the `awslambda` namespace.
With streaming, a procedure that returns an async generator, like `iterable` below, sends each value as it's yielded to clients using [`httpBatchStreamLink`](https://trpc.io/docs/client/links/httpBatchStreamLink).
```ts no-check title="server.ts"
/// <reference types="aws-lambda" />
import type { APIGatewayProxyEventV2 } from 'aws-lambda';
import type { CreateAWSLambdaContextOptions } from '/server/adapters/aws-lambda';
import { awsLambdaStreamingRequestHandler } from '/server/adapters/aws-lambda';
import { appRouter } from './router';
// created for each request
const createContext = ({
event,
context,
}: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2>) => ({
// your context
});
type Context = Awaited<ReturnType<typeof createContext>>;
export const handler = awslambda.streamifyResponse(
awsLambdaStreamingRequestHandler({
router: appRouter,
createContext,
}),
);
```
## Common mistakes
- **Using `httpBatchLink` with a resource per procedure.** A batch request puts several procedures in one path, like `/getUser,createUser`, which none of those resources match. Use one greedy resource like `/trpc/{proxy+}`, which tRPC reads the procedure path from, or switch to `httpLink`.
- **Forgetting `awslambda.streamifyResponse()`.** Without it, Lambda calls `awsLambdaStreamingRequestHandler` as a regular handler, which has no response stream to write to.
- **Expecting the default handler to stream.** `awsLambdaRequestHandler` buffers the whole response before returning it, so `httpBatchStreamLink` responses arrive all at once. Use the [streaming handler](https://trpc.io/docs/server/adapters/aws-lambda#response-streaming) instead.
## Common options
Every HTTP adapter accepts these options, next to its own (like `endpoint` for Fetch, or `basePath` for Standalone):
| Option | Description |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `router` | Your app router. Required. |
| `createContext` | Creates the [context](https://trpc.io/docs/server/context) for each request. It receives the adapter's own objects (e.g. `req` and `res`) plus `info`. |
| `onError` | Called with `{ error, type, path, input, ctx, req }` for every error, e.g. to report it. See [Error handling](https://trpc.io/docs/server/error-handling#handling-errors). |
| `responseMeta` | Returns `{ status, headers }` for the response, e.g. for [caching](https://trpc.io/docs/server/caching). |
| `allowBatching` | Set to `false` to reject batch requests. Defaults to `true`. |
| `maxBatchSize` | Rejects batch requests with more calls than this with a `400 Bad Request`. Set `maxItems` on your batch link to the same or a lower value. |
| `allowMethodOverride` | Accepts queries sent as `POST`, for clients that set `methodOverride: 'POST'` on their link. Defaults to `false`. |
The Node.js adapters (Standalone, Express and Next.js) also accept:
- `maxBodySize`: the largest request body in bytes; larger ones are rejected with `413 PAYLOAD_TOO_LARGE`. There's no limit by default; see [Request body size](https://trpc.io/docs/server/adapters#request-body-size).
- `middleware`: a connect-style `(req, res, next)` function that runs before tRPC, e.g. [`cors()`](https://trpc.io/docs/server/adapters/standalone#handling-cors--options).
```ts no-check title="server.ts"
import { createHTTPServer } from '/server/adapters/standalone';
import { appRouter } from './router';
createHTTPServer({
router: appRouter,
createContext: () => ({}),
onError({ error, path }) {
console.error(`tRPC error on '${path}':`, error);
},
maxBatchSize: 10,
maxBodySize: 1024 * 1024, // 1 MiB
}).listen(3000);
```
### Request body size
The Node.js adapters read the request body into memory, and `maxBodySize` is unset by default, so they accept bodies of any size. On a server that's reachable from the internet, set `maxBodySize` so a single large request can't exhaust the process's memory. A body over the limit is rejected with `413 PAYLOAD_TOO_LARGE` as soon as it passes the limit, before it's parsed.
We recommend `1024 * 1024` (1 MiB) for typical JSON inputs, as in the example above. If you accept files or other large inputs, set it a little above the largest one you expect, and keep it under about `10 * 1024 * 1024` (10 MiB).
`maxBodySize` only applies to bodies tRPC reads itself. If a body parser such as `express.json()` or the Next.js API routes body parser has already set `req.body`, limit the size in that parser instead. A limit in your reverse proxy (like nginx's `client_max_body_size`) adds another layer.
From https://trpc.io/docs/server/adapters#common-options
## Related skills
- `server-setup` (https://trpc.io/docs/server/overview): `npx @tanstack/intent@latest load @trpc/server#server-setup`