Skip to main content

Building a Chat Room with Lambda and Websocket in AWS API Gateway

cloud aws devops api-gateway websocket dynamodb lambda
Hugo
Author
Hugo
DevOps Engineer in London
Table of Contents

WebSocket Overview
#

  • WebSocket Characteristics
    • Bi-directional, persistent TCP connection between client and server
  • Scaling Limitations
    • WebSocket is stateful, cannot horizontally scale without a backend to store state (eg: Redis)

API Gateway with Websocket
#

  • Benefits

    • Aids scaling by maintaining WebSocket connections
    • Serverless - Client and Lambda interact with API Gateway
    • Lambda uses APIGatewayConnection API to send data to open connections
    • Client sends data to gateway directly
  • Limitations

    • WebSocket connection lasts for 2 hours
    • Idle timeout is 10 minutes

Lab: Building a Chat Room
#

Setup
#

I watched this tutorial on how to build a chat using Lambda, WebSocket, and API Gateway with Node.js

Access the code in this GitHub Gist

  • Server Setup

    • server.py is a simple HTTP server with Python to serve the client-side code
  • Lambda Function

    • index.mjs is the Lambda code for the chat room
    • Implements $connect, $disconnect, join, leave, and sendAll functions
    • Uses DynamoDB to store connectionIds and roomId with the connectionIds that joined

Common Errors and Solutions
#

Global Variable Persistence
#

  • The video stores the connections and rooms data in global variables in lambda.
  • Initially, it was thought that it would not work because each invocation is stateless.
  • However, variables in the global scope can be shared between Lambda invocations.
  • This is mentioned in the Comparing the effect of global scope.
  • However, it is more reliable to use a database for storing data.
  • The use of global variables should be limited to demo purposes only and should never be used in the real world.

Connection Endpoint
#

  • The endpoints can be found in the AWS Console under “API Gateway” > “Stages”.
  • One is WebSocket, starting with wss://, and another is HTTPS, starting with https://.
  • When posting the data back to the client from Lambda, use the HTTPS endpoint but remove @connections at the end.
  • For example, if the endpoint on the console is https://d6sq1c52q5.execute-api.us-east-1.amazonaws.com/production/@connections, we should omit the @connection when using PostToConnectionCommand.

Lambda Permissions
#

  • Lambda needs permission to call invoke and manage connection
  • Use AmazonAPIGatewayInvokeFullAccess or create a custom policy

Custom policy example:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "execute-api:Invoke",
                "execute-api:ManageConnections"
            ],
            "Resource": "arn:aws:execute-api:us-east-1:059035646743:d8ck9i69wa/production/*""
        }
    ]
}

Refer to the AWS documentation for more information.

“Going Away” Exception
#

  • You might encounter a “Going away” exception if you try to post data to a connection in $connect
  • This is not supported, as mentioned in this Stack Overflow post

Using Node.js 18 in Lambda Function
#

  • If you use Node.js 18, require('aws-sdk') will not work due to the use of ES module style
  • When connecting to the WebSocket, it may return a 502 error, which is also logged in the API Gateway access log but not useful for debugging purposes.
  • To diagnose the issue, check the Lambda error log on CloudWatch

To tail the CloudWatch log, use the following command:

aws logs tail <log_group> --follow

Refer to the AWS CLI documentation for more information.