I try to use lambdas with my AWS websocket api gateway.
I have made the following template.yaml
:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: "On connect and on disconnect handler"
Parameters:
DB_HOSTName:
Type: String
Default: '0.0.0.0:3306'
Description: Database host
DB_USERName:
Type: String
default: myuser
Description: Database User
DB_PASSWORDName:
Type: String
default: mypasswd
Description: Database Password
DB_NAME:
Type: String
default: mydb
Description: Database
Resources:
# Websocket API
OrderWebsocket:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: OrderWebsocket
ProtocolType: WEBSOCKET
RouteSelectionExpression: "$request.body.action"
# On Connect
ConnectRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref OrderWebsocket
RouteKey: $connect
AuthorizationType: NONE
OperationName: ConnectRoute
Target: !Join
- '/'
- - 'integrations'
- !Ref ConnectInteg
# Integrating on connect lambda into aws
ConnectInteg:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref OrderWebsocket
Description: Connect Integration
IntegrationType: AWS_PROXY
IntegrationUri:
Fn::Sub:
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnConnectFunction.Arn}/invocations
# On Disconnect lambda
DisconnectRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref OrderWebsocket
RouteKey: $disconnect
AuthorizationType: NONE
OperationName: DisconnectRoute
Target: !Join
- '/'
- - 'integrations'
- !Ref DisconnectInteg
# Integrating OnDisconnect Lambda into websocket
DisconnectInteg:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref OrderWebsocket
Description: Disconnect Integration
IntegrationType: AWS_PROXY
IntegrationUri:
Fn::Sub:
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${OnDisconnectFunction.Arn}/invocations
Deployment:
Type: AWS::ApiGatewayV2::Deployment
DependsOn:
- ConnectRoute
- DisconnectRoute
Properties:
ApiId: !Ref OrderWebsocket
Stage:
Type: AWS::ApiGatewayV2::Stage
Properties:
StageName: Prod
Description: Prod Stage
DeploymentId: !Ref Deployment
ApiId: !Ref OrderWebsocket
# Where onconnect function is located into our source code
OnConnectFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: onconnect/
Handler: app.handler
MemorySize: 256
Runtime: nodejs12.x
Environment:
Variables:
TABLE_NAME: !Ref TableName
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TableName
OnConnectPermission:
Type: AWS::Lambda::Permission
DependsOn:
- OrderWebsocket
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref OnConnectFunction
Principal: apigateway.amazonaws.com
# Where ondisconnect function is located into our source code
OnDisconnectFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ondisconnect/
Handler: app.handler
MemorySize: 256
Runtime: nodejs12.x
Environment:
Variables:
TABLE_NAME: !Ref TableName
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref TableName
OnDisconnectPermission:
Type: AWS::Lambda::Permission
DependsOn:
- OrderWebsocket
Properties:
Action: lambda:InvokeFunction
FunctionName: !Ref OnDisconnectFunction
Principal: apigateway.amazonaws.com
Outputs:
OnConnectFunctionArn:
Description: "OnConnect function ARN"
Value: !GetAtt OnConnectFunction.Arn
OnDisconnectFunctionArn:
Description: "OnDisconnect function ARN"
Value: !GetAtt OnDisconnectFunction.Arn
WebSocketURI:
Description: "The WSS Protocol URI to connect to"
Value: !Join [ '', [ 'wss://', !Ref OrderWebsocket, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/',!Ref 'Stage'] ]
And I try to test it locally without deploying it via the command:
sam local invoke OnConnectFunction
But I get the error:
Invoking app.handler (nodejs12.x)
Error: Could not find lambci/lambda:nodejs12.x image locally and failed to pull it from docker.
I also tried to launch it as a whole according to documentation but it failed as well:
$ sam local start-api
Error: Template does not have any APIs connected to Lambda functions
DO you have any idea why? And how I can invoke it locally, so I can test it in my computer first without need to deploy it?
The onconnect
function is the following:
const mysql = require('mysql');
const dbconnection = mysql.createConnection({
host: process.env.DB_HOST,
user: prossess.env.DB_USER,
password: prossess.env.DB_PASSWORD,
database: prossess.env.DB_NAME,
});
exports.handler = async event => {
// Api Gateway connection identifier
let connection_id = event.requestContext.connectionId;
let sql = "INSERT INTO websocket_connections (connection_id) VALUES (?)";
connection.connect();
try{
await connection.query(sql, [connection_id]);
} catch (err) {
console.log('Failed to store into database', err.toString());
return {statusCode: 500, body: 'Failed To Connect';}
}
connection.end();
return { statusCode: 200, body: 'Connected.' };
}
The ondisconnect
function is the following:
const mysql = require('mysql');
const dbconnection = mysql.createConnection({
host: process.env.DB_HOST,
user: prossess.env.DB_USER,
password: prossess.env.DB_PASSWORD,
database: prossess.env.DB_NAME,
});
exports.handler = async event => {
let connection_id = event.requestContext.connectionId;
let sql = "DELETE FROM websocket_connections WHERE connection_id = ?";
connection.connect();
try {
await connection.query(sql, [connection_id]);
} catch(err) {
// console.log output is written into cloudwatch logs.
console.log('Failed to store into database', err.toString());
return {statusCode: 500, body: 'Failed To Connect';}
}
connection.end();
return { statusCode: 200, body: 'Connected.' };
}
I just use a mysql database to store the client ids and once disconnected I remove them from my database.