Build Video/Chat App with AWS Websocket, WebRTC, and Vue Part 1
Tags: webdev, aws, tutorial, webrtc
Published: 2021-01-18 12:37:01

In today's tutorial, we will handle how to build a video and chat app with __AWS Websocket__, __AWS Kinesis__, __Lambda__, __Google WebRTC__, and __DyanamoDB__ as our database. There are so many products you can use to build a chat application. For video calls, you need to add the signaling capability to exchange WebRTC handshakes. That is where AWS WebSocket will come in handy. No infrastructure to manage but a robust API.

Other products that you can use

- AWS Amplify subscriptions
- Socket.io
- Pusher
- Firebase
- And many more

__AWS WebSocket APIs don't require repeated connection logic, and the client and server both have much less overhead once connected than they do with HTTP connections. HTTP polling is a technique that lets an application retrieve information based on a client pulling data__

I am a fan of serverless technology and AWS Websocket is one product that fills this gap when you want repeated connection logic. In essence with serverless, you don't need to worry about your infrastructure management just your application.  

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/0x5oqg590zy95haoonb6.png)

In the next tutorial, we will add video capability in our application using AWS Kinesis Video stream. 

#Pricing
__Pay only for messages sent and received and the total number of connection minutes. You may send and receive messages up to 128 kilobytes (KB) in size. Messages are metered in 32 KB increments. So, a 33 KB message is metered as two messages.__

__For WebSocket APIs, the API Gateway free tier includes one million messages (sent or received) and 750,000 connection minutes for up to 12 months.__

Thereafter 1billion requests 1 USD and the next 5billion request 0.80 USD.


# Brief explanation
Before you begin, here are a couple of the concepts of a WebSocket API in API Gateway. The first is a new resource type called a route. A route describes how API Gateway should handle a particular type of client request, and includes a routeKey parameter, a value that you provide to identify the route.

A WebSocket API is composed of one or more routes. To determine which route a particular inbound request should use, you provide a route selection expression. The expression is evaluated against an inbound request to produce a value that corresponds to one of your route’s routeKey values.

There are three special routeKey values that API Gateway allows you to use for a route:

- $default—Used when the route selection expression produces a value that does not match any of the other route keys in your API routes. This can be used, for example, to implement a generic error handling mechanism.
- $connect—The associated route is used when a client first connects to your WebSocket API.
- $disconnect—The associated route is used when a client disconnects from your API. This call is made on a best-effort basis.

You are not required to provide routes for any of these special routeKey values.


You need an AWS account for this tutorial here is a link to register [https://portal.aws.amazon.com/billing/signup#/start](https://portal.aws.amazon.com/billing/signup#/start).


# Vue Application.

AWS Websocket tutorials I have gone through last year when I was preparing for my AWS Solution Artichect exams had one problem they only showed you how to configure an AWS Websocket but as a Developer, you need to know how to integrate it into your application. This what this tutorial will help through with. The application will have these sections;

###Chat Section 

Users will be able to create a channel or group like Telegram and chat with one another.

  ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/65qkwit809qy29xdkgua.png)

###Meeting Section
This section will allow us to create a meeting or join a meeting. The video stream will be handled with AWS Kinesis Video Stream. You can use any other API to handle this.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/pbhc7lm8rj0897j4rglu.png)

###Video Section 
This section will handle the video section for both the meeting and chats that switches to video calls.

Here is a GitHub link [https://github.com/kevinodongo/video-chat-app.git](https://github.com/kevinodongo/video-chat-app.git) to the above application that you can use for this tutorial.

# Configure AWS Backend

We have a Vue App now let us configure our Backend in AWS and integrate our application to make it functional.

We will configure 3 Lambda functions:
- connect_app
- disconnect_app
- onMessage

Let us create a table in Dynamo DB table called chat_app_table then create the functions as follows.

# connect_app
This Lambda function will update our database once the $connect route request from users to the WebSocket API. Once connected the connection id will be saved persistently in the Dynamo DB

```javascript
const AWS = require('aws-sdk');

let send = undefined;
function onConnect(event) {
    var dynamodb = new AWS.DynamoDB();
    send = async (data) => {
        await dynamodb.putItem(data).promise();
    }
}

// event
exports.handler = async (event) => {
   onConnect(event);
   var params = {
        TableName: "chat_app_table", // Table Name
        Item: {
            connectionId: { S: event.requestContext.connectionId } // connection ID

        }
    };
   await send(params);
    var msg = 'connected';
    return { 
        statusCode: 200, 
        body: JSON.stringify({ msg: msg}) /*required on lambda proxy integration*/
    };
};
```

# disconnect_app 
This Lambda function will update our database once the $disconnect route request from users to the WebSocket API. Once disconnected the connection id will be deleted from the Dynamo DB


```javascript
const AWS = require('aws-sdk');

let send = undefined;
function onConnect(event) {
    var dynamodb = new AWS.DynamoDB();
    send = async (data) => {
        await dynamodb.deleteItem(data).promise();
    }
}

// event
exports.handler = async (event) => {
   onConnect(event);
   var params = {
        TableName: "chat_app_table", // Table Name
        Key: {
            connectionId: { S: event.requestContext.connectionId } // connection ID

        }
    };
   await send(params);
    var msg = 'disconected';
    return { 
        statusCode: 200, 
        body: JSON.stringify({ msg: msg}) /*required on lambda proxy integration*/
    };
};
```

# onMessage
This Lambda function will handle the exchange of messages between users. 

```javascript
const AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB();

let send = undefined;
function init(event) {
  const apigwManagementApi = new AWS.ApiGatewayManagementApi({
    apiVersion: '2018-11-29',
    endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
  });
  send = async (connectionId, data) => {
    await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: `${data}` }).promise();
  }
}

function onScan(err, data) {
    if (err) {
        console.error("Unable to scan the table. Error JSON:", JSON.stringify(err, null, 2));
    } else {
        // print all 
        console.log("Scan succeeded.", data);
        data.Items.forEach(function(data) {
            console.log('DATA', data)
        });

        // continue scanning if we have more movies, because
        // scan can retrieve a maximum of 1MB of data
        if (typeof data.LastEvaluatedKey != "undefined") {
            console.log("Scanning for more...");
            //params.ExclusiveStartKey = data.LastEvaluatedKey;
          //  dynamodb.scan(params, onScan);
        }
    }
}

exports.handler = async(event) => {
  // send the event
  init(event);
  
  // scan dynamodb table
  var scanParams = {
      TableName: "chat_app_table", // Table Name
      ExpressionAttributeNames: {
        "#ID": "connectionId"
      },
      ExpressionAttributeValues: {
         ":connectionId": {
           S: event.requestContext.connectionId
         },
      },
      FilterExpression: "connectionId = :connectionId", 
       ProjectionExpression: "#ID",
    };
  await dynamodb.scan(scanParams, onScan)
  const connectionId = event.requestContext.connectionId;
  let data = JSON.parse(event.body).data
  await send(connectionId, data);
  // the return value is ignored when this function is invoked from WebSocket gateway
  return {};
};
```

Update the Lamda roles by allowing permission to interact with Dynamo DB and invoke API Gateway.

```
// Add this to your Lambda roles
{
            "Effect": "Allow",
            "Action": "execute-api:*",
            "Resource": "arn:aws:execute-api:us-east-2:[ID]:*/*/*/*"
        },
        {
            "Sid": "DynamoDBTableAccess",
            "Effect": "Allow",
            "Action": [
                "dynamodb:PutItem",
                "dynamodb:DescribeTable",
                "dynamodb:DeleteItem",
                "dynamodb:GetItem",
                "dynamodb:Scan",
                "dynamodb:Query",
                "dynamodb:UpdateItem"
            ],
            "Resource": "arn:aws:dynamodb:us-east-2:[ID]:table/[TABLE_NAME]"
        },
```

With three functions in place lets us create an AWS Websocket through the console and test our backend. For integration, we will be creating the API through the application. Ensure you have created the dynamo DB table that connection ids will always be saved during the connection. For chatting without Video will have to save the messages as well for future reference. Our Lambda function will be updated further as we go along with the tutorial.

For testing, you will need to install the following package wscat

```
yarn add wscat
```

Go to API Gateway dashboard then Search for API Gateway and select Websocket

1. Choose a name 
2. For Route Selection Expression, enter $request.body.action. 
3. Choose Lambda as you integrate and select each Lambda you created for each routing
4. Then Create API

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mmseolpvawjagwbnhgmm.png)

####NOTE
You need to add a role for Lambda to able to invoke Lambda 

```
// Provides full access to invoke APIs in Amazon API Gateway.
arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess
```

Once you have saved go to your stage and get the URL with wss://

```bash
wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/{STAGE}
```

```bash
$ wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/development
connected (press CTRL+C to quit)
> {"action":"sendmessage", "data":"hello world"}
< hello world
```

That indicates we have connected successfully and we are able to send a message through the API. Anyone who is connected to the Socket will receive this message.

Here is a quick video to guide you in completing the above steps. 
{% youtube 5ZC0k4yDP-U %}


I hope these tutorials are helpful and you will be able to configure your Lambda functions, API Gateway, and Dynamo DB Table.

Thank you and see you in the next tutorial of integrating our application and make it function.