How to build a React chat app with AWS API Gateway WebSockets, Custom Lambda Authorizer

AWS announced the launch of a widely-requested feature: WebSockets for Amazon API Gateway few days ago. To test out this new feature, I spent a couple of hours building a realtime chat App using WebSockets with custom lambda authorizer.

Before You Start

Please read below articles to understand WebSocket APIs in Amazon API Gateway and Cognito User Pool

Now, Let’s Start

You can find all the code on GitHub

Main Libraries in Backend

  • API Gateway WebSockets
  • DynamoDB
  • Cognito User Pool
  • Serverless 1.35.1
  • Node.js 8.10

Main Libraries in Frontend

Structure

Issue

I always get 500 error when trying to connect to WebSockets with authoriztion if authorizer lambda function and WebSockets functions are in same project, so i split authorizer lambda function and WebSockets lambda functions to two Serverless projects.

There are three sub projects:

Backend

API Gateway WebSockets and lambda functions to manage WebSockets routes ($connect, $disconnect, sendMessage) and create DynamoDb to store WebSockets connectionIds.

Auth

Custom authorizer lambda function.

Frontend

React online app with live chat functionality.

Building API, the Serverless Framework, and AWS Lambda

You’ll need to have serverless-websockets-pluginplugin in your serverless.yml plugins listing:

serverless-websockets-plugin

Your serverless.yml might look something like the following

provider:
name: aws
runtime: nodejs8.10
iamRoleStatements:
- Effect: Allow
Action:
- "execute-api:ManageConnections"
Resource:
- "arn:aws:execute-api:*:*:**/@connections/*"
  websocketApiName: websocket-chat-${self:provider.stage}
  # required for WebSocket APIs
websocketApiRouteSelectionExpression: $request.body.action
plugins:
- serverless-websockets-plugin
functions:
connectionManager:
handler: handler.connectionManager
events:
- websocket:
routeKey: $connect
- websocket:
routeKey: $disconnect
defaultMessages:
handler: handler.defaultMessage
events:
- websocket:
routeKey: $default
  sendMessage:
handler: handler.sendMessage
events:
- websocket:
routeKey: sendMessage

Deploy Backend and Auth Serverless Projects

Go to Backend and Auth directory, run

sls deploy --stage dev

API and lambda functions will be created automatically after deployment.

Configure REQUEST Authorizer function

Serverless Framework doesn’t support custom authorizer for WebSockets route yet, to configure a lambda function as a REQUEST authorizer for a WebSocket API, follow below steps:

  1. Go to AWS console, find api deployed in previous step, in this example the api name is websocket-chat-dev
  2. Go to Authorizers-> Create Authorizer,
  3. Set lambda function to serverless-chat-auth-dev-authorizerFunc which is deployed in previous step .
  4. Set Identity Sources to Query String, value is query string variable, i set to token in this example.
  5. Save.

Next, Set Authorizer to $connect route

  1. Go to Routes->$connect->Route Request
  2. Set auth to Authorization and Save

3. Deploy API from AWS API gateway console.

React Frontend Implementation

AWS Amplify (1.1.17) pubsub module doesn’t support API Gateway WebSockets yet. I decide to use sockette as WebSockets client. It is a tiny library and truly simple to use.

Below are example of declaring all event listeners on initialization and sending messages:

import Sockette from "sockette";
//Init WebSockets with Cognito Access Token
ws = new Sockette(
"wss://APP_CLIENT_ID.execute-api.ap-southeast-2.amazonaws.com/dev?token=" +
props.authData.signInUserSession.accessToken.jwtToken,
{
timeout: 5e3,
maxAttempts: 1,
onopen: e => console.log("connected:", e),
onmessage: e => console.log("Message Received:", e),
onreconnect: e => console.log("Reconnecting...", e),
onmaximum: e => console.log("Stop Attempting!", e),
onclose: e => console.log("Closed!", e),
onerror: e => console.log("Error:", e)
}
);
//Send Message
ws.json({
action: "sendMessage",
data: "Hello World"
});

React Hooks

In Frontend project, I have no Class components by integrating React Hook, Hooks make it possible to take a React function component and add state to it, or hook into lifecycle methods like componentDidMount and componentDidUpdate.

At the version of 16.7.0-alpha.2, Hooks are in alpha. Their API can change at any time. I recommend you experiment, have fun, and use Hooks in your side projects, but not in production code until they’re stable.

All set to go, Try it out!

Go to frontend directory, run

$ npm install
$ npm start

Preview