Real-Time Connections with AWS
Tags: aws, infrastructureascode, websocket, tutorial
Published: 2023-08-22 15:15:31

AWS API Gateway offers a WebSocket API feature that can simplify the process of setting up real-time, two-way communication between clients and servers. 

With this feature, developers don't need to manage the underlying infrastructure or handle WebSocket connections directly.

![Sequence diagram for WebSockets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3qy96bpsq6dfj0m6f6vm.png)

This sequence diagram shows the following steps:

1. The client sends an HTTP request with an "Upgrade: websocket" header to AWS API Gateway.
2. API Gateway responds with a "101 Switching Protocols" response, establishing a WebSocket connection with the client.
3. The client sends a WebSocket message to API Gateway.
4. API Gateway invokes the appropriate AWS Lambda function based on the route key defined in the message.
5. The Lambda function processes the message and returns the response to API Gateway.
6. API Gateway forwards the response back to the client.
7. To close the connection, the client sends a close frame, and API Gateway acknowledges it by sending a close frame back to the client, which closes the WebSocket connection.

## Practical Use Cases for WebSockets

- Real-Time Dashboards: Allows live streaming of data for stock updates, sports scores, and IoT sensor data.
- Collaborative Applications: Supports synchronized interactions and real-time collaboration.
- Chat and Messaging Applications: Assures instant message delivery and real-time conversations.

## Let's Build a Messaging App with WebSockets

By the end of this tutorial, you'll have a functioning WebSocket server that you can use as a foundation for more advanced applications.

We'll start by importing our resources `websocket` and `collection`.

```typescript
import { websocket, collection } from "@nitric/sdk";
```

We'll use a collection to keep track of the client's connection info. This is essentially a document DB holding onto all of the connection ids.

You'll notice that we're granting permissions here for read, write, and delete. These will translate to IAM roles and permissions at deploy time to make sure we have access to the db.

```typescript
const connections = collection("connections").for(
  "reading",
  "writing",
  "deleting"
);
```

Next, initialize the WebSocket server and implement our connect, disconnect, and message event handlers.

```typescript
const socket = websocket("socket");
``` 

You can learn a little more about the types of events that AWS Websockets supports by [visiting their docs](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-overview.html).

Here's a snippet which describes what events are supported 

> There are three predefined routes that can be used: $connect, $disconnect, and $default. In addition, you can create custom routes.

> API Gateway calls the $connect route when a persistent connection between the client and a WebSocket API is being initiated.

> API Gateway calls the $disconnect route when the client or the server disconnects from the API.

![AWS Components for a simple messaging application
](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1g2erss8snmoxlw7gpaj.png)



Handle new client connections:

```typescript 
socket.on("connect", async (ctx) => {
    // Store the new connection in the collection
    await connections.doc(ctx.req.connectionId).set({});
});
```

Handle client disconnections:

```typescript 
socket.on("disconnect", async (ctx) => {
    // Remove the disconnected client from the collection
    await connections.doc(ctx.req.connectionId).delete();
});
```

Handle incoming messages from clients, here the server will broadcast messages sent from one client to all other connected clients.

```typescript 
socket.on("message", async (ctx) => {
    // Get all connections from the collection
    const allConnections = await connections.query().stream();
    
    // Create a promise that resolves when the stream ends
    const streamEnd = new Promise<any>((res) => allConnections.on("end", res));
    
    //Broadcast the incoming message to all connected clients except the sender
    allConnections.on("data", async (connection) => {
        if (connection.id !== ctx.req.connectionId) {
            await socket.send(ctx.req.connectionId, ctx.req.data);
        }
    });
    
    // Wait for the stream to end
    await streamEnd;
});
```

[Access the full source here](https://github.com/nitrictech/example-apps/tree/main/websockets).

Follow the readme to get it up and running. You can test it using a WebSocket client like Postman to connect to your server. Each client will connect with a unique connection id, allowing you to send messages between multiple clients connected to your server.

That's pretty much it, you should have the fundamentals to build upon to make a more comprehensive application, which communicates in real-time and runs on AWS.