WebSocket Handlers
defineWebSocketHandler creates Lambda handlers for API Gateway WebSocket routes. Import from @mantleframework/core.
Each WebSocket route ($connect, $disconnect, $default, or a custom route key) maps to a separate Lambda handler. The factory extracts connectionId, routeKey, and body from the raw event and passes them as typed params.
Pattern
// src/lambdas/websocket/connect/index.ts
import { defineWebSocketHandler } from '@mantleframework/core'
import { storeConnection, removeConnection, broadcastToConnections } from '@mantleframework/aws'
// $connect — store connection on client connect
const connectHandler = defineWebSocketHandler({ routeKey: '$connect', operationName: 'WsConnect' })
export const handler = connectHandler(async ({ connectionId, requestContext }) => {
await storeConnection(connectionId, {
userId: requestContext.authorizer?.userId,
connectedAt: requestContext.connectedAt,
})
return { statusCode: 200 }
})
// src/lambdas/websocket/disconnect/index.ts
// $disconnect — clean up on client disconnect
const disconnectHandler = defineWebSocketHandler({ routeKey: '$disconnect', operationName: 'WsDisconnect' })
export const handler = disconnectHandler(async ({ connectionId }) => {
await removeConnection(connectionId)
return { statusCode: 200 }
})
// src/lambdas/websocket/default/index.ts
// $default — broadcast incoming messages to all active connections
const defaultHandler = defineWebSocketHandler({ routeKey: '$default', operationName: 'WsDefault' })
export const handler = defaultHandler(async ({ connectionId, body }) => {
await broadcastToConnections({ from: connectionId, payload: body })
return { statusCode: 200 }
})Options
| Option | Type | Default | Description |
|---|---|---|---|
routeKey | '$connect' | '$disconnect' | '$default' | string | (required) | WebSocket route key this handler responds to |
operationName | string | function name | Name for metrics and tracing |
logging | LoggingConfig | -- | Fixture logging configuration |
timeout | number | 29 | Lambda timeout in seconds (WebSocket max is 29s) |
memorySize | number | 128 | Lambda memory in MB |
reservedConcurrency | number | -- | Reserved concurrent executions |
ephemeralStorage | number | 512 | Ephemeral storage in MB |
deadLetterQueue | boolean | { targetArn? } | -- | true = auto-generate SQS DLQ |
retryAttempts | number | -- | Max retry attempts (0-2) |
Handler Params
interface WebSocketHandlerParams {
event: APIGatewayProxyWebsocketEventV2
context: Context
metadata: WrapperMetadata // { traceId, correlationId }
connectionId: string // API Gateway connection ID
routeKey: string // Route key that triggered this handler
body: unknown // Parsed JSON if present, raw string, or null
requestContext: APIGatewayEventWebsocketRequestContextV2
}The handler must return WebSocketResult:
interface WebSocketResult {
statusCode: number // Use 200 for success
body?: string // Optional response body
}Connection Management
@mantleframework/aws provides a DynamoDB-backed connection store. Requires CONNECTIONS_TABLE_NAME env var pointing to a DynamoDB table with connectionId (S) as hash key, plus connectedAt (N) and ttl (N) attributes.
| Function | Description |
|---|---|
storeConnection(connectionId, metadata?, ttlSeconds?) | Persist a new connection. Default TTL: 24 hours. |
removeConnection(connectionId) | Remove a connection on disconnect. |
getConnection(connectionId) | Retrieve a single connection record by ID. |
getActiveConnections() | Return all connections whose TTL has not expired. |
broadcastToConnections(data, options?) | Send a JSON payload to all active connections. |
interface ConnectionRecord {
connectionId: string
connectedAt: number // Unix timestamp (seconds)
ttl: number // Unix timestamp when connection expires
metadata?: ConnectionMetadata
}
interface BroadcastOptions {
endpoint?: string // API Gateway WebSocket endpoint (defaults to WEBSOCKET_API_ENDPOINT env var)
cleanupStale?: boolean // Remove stale connections returning GoneException. Default: true
}broadcastToConnections uses Promise.allSettled internally and removes stale connections (GoneException / HTTP 410) automatically when cleanupStale is true (the default).
Custom Route Keys
Beyond the three standard routes, any string can be a route key:
const chatHandler = defineWebSocketHandler({ routeKey: 'chat', operationName: 'WsChat' })
export const handler = chatHandler(async ({ connectionId, body }) => {
const message = body as { text: string; room: string }
await ChatQueries.saveMessage(message.room, connectionId, message.text)
return { statusCode: 200 }
})File Location
WebSocket handlers live under src/lambdas/websocket/<route>/index.ts:
src/lambdas/websocket/
connect/
index.ts
disconnect/
index.ts
default/
index.ts
chat/
index.tsSee Also
- Handler Patterns — overview of all handler types
- API Handlers — HTTP endpoint handlers