AWS Lambda Adapter
Use oRPC on AWS Lambda behind API Gateway (payload formats 1.0 and 2.0) and Lambda Function URLs using response streaming.
Server Usage
import type { APIGatewayProxyEventV2, AwsLambdaGlobal } from '@standard-server/aws-lambda'
import { onError } from '@orpc/server'
import { RPCHandler } from '@orpc/server/aws-lambda'
import { CORSHandlerPlugin } from '@orpc/server/plugins'
declare const awslambda: AwsLambdaGlobal
const handler = new RPCHandler(router, {
plugins: [
new CORSHandlerPlugin()
],
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
export const rpc = awslambda.streamifyResponse<APIGatewayProxyEventV2>(async (event, responseStream, context) => {
const { matched } = await handler.handle(event, responseStream, {
prefix: '/rpc',
context: {} // Provide initial context if needed, can be an async function
})
if (matched) {
return
}
awslambda.HttpResponseStream.from(responseStream, {
statusCode: 404,
headers: {},
cookies: [],
}).end('Not found')
})import type { APIGatewayProxyEventV2, AwsLambdaGlobal } from '@standard-server/aws-lambda'
import { OpenAPIHandler } from '@orpc/openapi/aws-lambda'
import { onError } from '@orpc/server'
import { CORSHandlerPlugin } from '@orpc/server/plugins'
declare const awslambda: AwsLambdaGlobal
const handler = new OpenAPIHandler(router, {
plugins: [
new CORSHandlerPlugin()
],
interceptors: [
onError((error) => {
console.error(error)
}),
],
})
export const api = awslambda.streamifyResponse<APIGatewayProxyEventV2>(async (event, responseStream, context) => {
const { matched } = await handler.handle(event, responseStream, {
prefix: '/api',
context: {} // Provide initial context if needed, can be an async function
})
if (matched) {
return
}
awslambda.HttpResponseStream.from(responseStream, {
statusCode: 404,
headers: {},
cookies: [],
}).end('Not found')
})Path Encoding
API Gateway HTTP APIs deliver the request path (rawPath in payload 2.0, path in payload 1.0) already URL-decoded, while REST APIs, ALB, and Lambda Function URLs deliver it still encoded. The handler re-escapes decoded paths before matching, so every path is decoded only once and a literal %61 in an HTTP API path stays %61 instead of becoming a.
By default ('auto'), a payload 1.0 event is treated as decoded when it carries version: '1.0' (HTTP API), and a payload 2.0 event is treated as decoded unless requestContext.domainName is a Function URL host (*.lambda-url.<region>.on.aws). Set pathEncoding to override the detection:
const handler = new RPCHandler(router, {
/**
* 'auto': detect from the event (default)
* 'encoded': every path is still URL-encoded
* 'decoded': every path is already URL-decoded
*
* @default 'auto'
*/
pathEncoding: 'auto',
})
Event Stream Options
You can configure how an AsyncIteratorObject is streamed to the client using the sendStandardResponse.eventStream options when creating the handler.
const handler = new OpenAPIHandler(router, {
sendStandardResponse: {
eventStream: {
initialComment: {
/**
* If true, an initial comment is sent immediately upon stream start to flush headers.
* This allows the receiving side to establish the connection without waiting for the first event.
*
* @default true
*/
enabled: true,
/**
* The content of the initial comment sent upon stream start. Must not include newline characters.
*
* @default ''
*/
comment: '',
},
keepAlive: {
/**
* If true, a ping comment is sent periodically to keep the connection alive.
*
* @default true
*/
enabled: true,
/**
* Interval (in milliseconds) between ping comments sent after the last event.
*
* @default 15000
*/
interval: 15000,
/**
* The content of the ping comment. Must not include newline characters.
*
* @default ''
*/
comment: '',
},
},
},
})