API-Gateway
The Endpoints
After we have built websocket-apis from API-Gateway, we get the endpoints from:
Here the one with wss
is for WebSocket
api, and the one with https
is for POST
request or aws-sdk
.
Routes (routeKeys) available for the https-endpoint
By default we have $connect
, $disconnect
and $default
:
Although we have configured custom routeKey
, but it is no more convenient than setting a data.action
. Therefore we can omit it and relies on $default
.
Quick link for the cloudwatch of the corresponding lambda function.
Edit execution role in order to send messages to frontend
With the default role we will get the following error when we try to send a message to a connnectionId
(we explain how to do it later):
User: arn:aws:sts::562976154517:assumed-role/websocket-testing-role-vkzcsju3 /websocket-testing is not authorized to perform: execute-api:ManageConnections on resource: arn:aws:execute-api:ap-northeast-1:********4517:3hfbt7ivk0/production /POST/@connections/{connectionId}",
Here we can identify:
562976154517
is our AWS Account ID;3hfbt7ivk0
is our API Gateway API ID;
repsectively, therefore we need to adjust the execution role of the lambda:
We create an inline
-policy in json format:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["execute-api:ManageConnections"], "Resource": [ "arn:aws:execute-api:ap-southeast-2:798404461798:smcflfkjb6/dev/POST/@connections/*" ] } ] }
Basically when we provide the error log to LLM, we will get the related configuration.
Implementation
Procedures to get connectionId
in frontend
connectionId
in frontendHere we get our connectionId
in 3 steps:
-
We first connect by calling the api:
new WebSocket("wss://<domain-name>/production/");
-
We listen to
open
(i.e., connected) event, we then send an event to request forconnectionId
by sending{ action: GET_CONNECTION_ID }
-
We listen to
message
and get theconnectionId
onceJSON.parse(event?.data || "{}")?.connectionId;
exists.
Now we can request to join any chatroom using our own connectionId
.
Frontend: Get connectionId
from frontend
connectionId
from frontend1 useEffect(()=>{ 2 const socket = new WebSocket('wss://<domain-name>/production/'); 3 socket.addEventListener('open', (_event) => { 4 console.log("getting connection id ...") 5 socket.send(JSON.stringify({ action: "GET_CONNECTION_ID" })); 6 });
We have dispatched a GET_CONNECTION_ID
event, next let's wait for the return:
7 socket.addEventListener('message', (event) => { 8 console.log("message recevied") 9 const messageData = JSON.parse(event?.data || "{}") as {connectionId?: string} 10 if (messageData.connectionId) { 11 setConnectionId(messageData.connectionId) 12 } 13 }); 14 }, [])
Code of lambda connected to websocket-api
// index.mjs import { ApiGatewayManagementApiClient, PostToConnectionCommand, } from "@aws-sdk/client-apigatewaymanagementapi"; export const handler = async (event) => { const connectionId = event.requestContext.connectionId; const routeKey = event.requestContext.routeKey; const eventType = event.requestContext.eventType; console.log("is it a normal event?", event); const action = JSON.parse(event?.body || "{}")?.action; if (action === "GET_CONNECTION_ID") { const callbackUrl = `https://${event.requestContext.domainName}/${event.requestContext.stage}`; const clientApi = new ApiGatewayManagementApiClient({ endpoint: callbackUrl, }); const requestParams = { ConnectionId: connectionId, Data: JSON.stringify({ connectionId }), }; const command = new PostToConnectionCommand(requestParams); await clientApi.send(command); } const response = { statusCode: 200, }; return response; };