Replies: 1 comment
-
|
AWS Lambda doesn't support persistent WebSocket connections directly — Lambda functions are stateless and short-lived. For WebSockets on AWS, you need API Gateway WebSocket APIs, which manage the persistent connections while Lambda handles individual events. ArchitectureAPI Gateway maintains the WebSocket connections. Your Lambda gets invoked for three event types: Implementation1. Separate your HTTP and WebSocket handlers// apps/backend/lambda/app.ts (HTTP - stays the same)
import { Hono } from "hono/quick";
const app = new Hono().basePath("/api");
app.route("/auth", authRoutes);
export default app;// apps/backend/lambda/ws-handler.ts (WebSocket)
import {
APIGatewayProxyWebsocketHandlerV2,
APIGatewayProxyResultV2,
} from "aws-lambda";
import {
ApiGatewayManagementApiClient,
PostToConnectionCommand,
} from "@aws-sdk/client-apigatewaymanagementapi";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
PutCommand,
DeleteCommand,
ScanCommand,
DynamoDBDocumentClient,
} from "@aws-sdk/lib-dynamodb";
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const TABLE_NAME = process.env.CONNECTIONS_TABLE!;
export const handler: APIGatewayProxyWebsocketHandlerV2 = async (event) => {
const connectionId = event.requestContext.connectionId!;
const routeKey = event.requestContext.routeKey;
switch (routeKey) {
case "$connect":
await ddb.send(
new PutCommand({
TableName: TABLE_NAME,
Item: {
connectionId,
connectedAt: Date.now(),
// Store auth info from query params or headers
userId: event.queryStringParameters?.userId,
},
})
);
return { statusCode: 200, body: "Connected" };
case "$disconnect":
await ddb.send(
new DeleteCommand({
TableName: TABLE_NAME,
Key: { connectionId },
})
);
return { statusCode: 200, body: "Disconnected" };
case "$default":
// Handle incoming messages
const body = JSON.parse(event.body || "{}");
const endpoint = `https://${event.requestContext.domainName}/${event.requestContext.stage}`;
const apigw = new ApiGatewayManagementApiClient({ endpoint });
// Echo back or broadcast
await apigw.send(
new PostToConnectionCommand({
ConnectionId: connectionId,
Data: Buffer.from(
JSON.stringify({ message: "received", data: body })
),
})
);
return { statusCode: 200, body: "OK" };
}
return { statusCode: 200, body: "OK" };
};2. Infrastructure (SAM/CDK)# template.yaml (SAM)
Resources:
WebSocketApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: MyWebSocketApi
ProtocolType: WEBSOCKET
RouteSelectionExpression: "$request.body.action"
ConnectionsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: ws-connections
AttributeDefinitions:
- AttributeName: connectionId
AttributeType: S
KeySchema:
- AttributeName: connectionId
KeyType: HASH
BillingMode: PAY_PER_REQUEST
WebSocketHandler:
Type: AWS::Serverless::Function
Properties:
Handler: ws-handler.handler
Runtime: nodejs20.x
Environment:
Variables:
CONNECTIONS_TABLE: !Ref ConnectionsTable
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref ConnectionsTable
- Statement:
- Effect: Allow
Action: execute-api:ManageConnections
Resource: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${WebSocketApi}/*"3. Local development with WebSocketsFor local dev, keep using // apps/backend/lambda/index.ts (local dev only)
import { serve } from "@hono/node-server";
import { createNodeWebSocket } from "@hono/node-ws";
import app from "./app";
const { injectWebSocket, upgradeWebSocket } = createNodeWebSocket({ app });
app.get(
"/ws",
upgradeWebSocket(() => ({
onMessage(event, ws) {
ws.send(JSON.stringify({ echo: event.data }));
},
}))
);
const server = serve({ fetch: app.fetch, port: 3000 });
injectWebSocket(server);Summary
You cannot use Hono's WebSocket helpers in Lambda because Lambda doesn't maintain persistent connections. The API Gateway WebSocket API is the AWS-native way to handle this. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I am working on an API where I need to have a websocket route. Since I need to deploy on AWS Lambda, I am looking to find the right way to set it up.
To run things locally for development, I use the node runtime. I have three main files:
As you can see,
app.tsholds the server logic,index.tsallows me to run the server locally and I usehandler.tsfor deployment on lambda via cdk.Here is where I am struggling:
I am struggling here as
createNodeWebSocketcomes from@hono/node-wsbutapp.tsneeds to be runtime agnostic so it can be used in node for local dev on in AWS Lambda for production. I am also not sure how, where or when to callinjectWebSocket.If I am thinking about this completely incorrectly, please let me know.
Beta Was this translation helpful? Give feedback.
All reactions