Karolis Stulgys
05/23/2022, 4:32 PMconnect
handler and send a message or how is that done?Ross Coundon
05/23/2022, 4:35 PMimport * as sst from '@serverless-stack/resources';
import {RemovalPolicy} from 'aws-cdk-lib';
export default class WebSocketStack extends sst.Stack {
private api: sst.WebSocketApi;
private readonly connectionsTable: sst.Table;
constructor(scope: <http://sst.App|sst.App>, id: string, props?: sst.StackProps) {
super(scope, id, props)
this.connectionsTable = this.createConnectionsTable();
this.api = this.createApi();
this.api.attachPermissions([this.connectionsTable]);
this.addOutputs({
ApiEndpoint: this.api.url
})
}
private createConnectionsTable(): sst.Table {
return new sst.Table(this, 'connections-table', {
dynamodbTable: {
removalPolicy: RemovalPolicy.DESTROY,
timeToLiveAttribute: 'ttl',
},
fields: {
PK: sst.TableFieldType.STRING,
connectionId: sst.TableFieldType.STRING,
ttl: sst.TableFieldType.NUMBER,
},
primaryIndex: {
partitionKey: 'PK',
}
})
}
private createApi(): sst.WebSocketApi {
return new sst.WebSocketApi(this, 'WebsocketApi', {
accessLog: false,
routes: {
$connect: "src/main/handlers/wsHandlers.webSocketHandler",
$disconnect: "src/main/handlers/wsHandlers.webSocketHandler",
sendmessage: "src/main/handlers/wsHandlers.webSocketHandler",
$default: "src/main/handlers/wsHandlers.webSocketHandler",
},
defaultFunctionProps: {
environment: {
CONNECTIONS_TABLE: this.connectionsTable.tableName,
REGION: this.region,
},
}
})
}
}
Handlers like
import { APIGatewayProxyHandler } from 'aws-lambda'
import { ApiGatewayManagementApiClient, PostToConnectionCommand } from '@aws-sdk/client-apigatewaymanagementapi'
import { ConnectionsDao } from '@/transit/db/connectionsTable/ConnectionsDao'
export const webSocketHandler: APIGatewayProxyHandler = async (event) => {
console.log(event);
const { body, requestContext: { connectionId, routeKey }} = event;
console.log(body, routeKey, connectionId);
if(!connectionId) {
return {
statusCode: 400,
body: JSON.stringify({
message: 'ConnectionId is required'
})
}
}
switch (routeKey) {
case '$connect':
await ConnectionsDao.save(connectionId);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Connected'
})
}
case '$disconnect':
await ConnectionsDao.delete(connectionId);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Disconnected'
})
};
case 'sendmessage': {
let messageData = {};
if(event.body){
messageData = JSON.parse(event.body).data;
}
const { stage, domainName } = event.requestContext;
// Get all the connections
const connections = await ConnectionsDao.getAll();
const apigw = new ApiGatewayManagementApiClient({
region: 'eu-west-2',
apiVersion: "2018-11-29",
endpoint: `https://${domainName}/${stage}`,
});
const sendPromises = connections.map(connection => {
const command = new PostToConnectionCommand({
ConnectionId: connection.connectionId,
// @ts-ignore
Data: JSON.stringify(messageData),
})
return apigw.send(command);
})
await Promise.allSettled(sendPromises);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Sent'
})
}
}
case '$default': {
console.log('nothing to see here');
return {
statusCode: 401,
body: JSON.stringify({
message: '$default not in use'
})
}
}
}
return {
statusCode: 400,
body: JSON.stringify({
message: 'Unknown routeKey'
})
}
}
Karolis Stulgys
05/23/2022, 4:38 PMKlaus
05/23/2022, 4:44 PMFrank
sst start
sends a msg back on connect as @Klaus suggested https://github.com/serverless-stack/serverless-stack/blob/master/packages/core/src/runtime/ws.ts#L72-L76