Building a Serverless Real-Time Chat Application with Golang and AWS

Building a Serverless Real-Time Chat Application with Golang and AWS

Creating a Real-time Chat Application with Golang, AWS API Gateway, and AWS Lambda: A Step-by-Step Guide

ยท

5 min read

The concept of real-time applications is nothing new. We're all familiar with Facebook, Twitter, WhatsApp, and many more applications that have real-time features. These applications notify us instantly when a new message or notification comes in.

In this blog post, I will guide you through the process of creating a simple chat application using a Golang HTTP server and AWS services such as API Gateway and AWS Lambda. The chat application we'll be building is going to be a basic one: opening a connection, sending requests, and receiving responses. However, these fundamental concepts can be expanded into more complex, real-world applications.

Let's get started!

Here is a video about this topic ๐Ÿš€

Part 1: Building the Golang Server ๐Ÿ”ฎ

The first step in our journey is to build an HTTP server in Golang. Golang, or Go, is a statically typed, compiled language renowned for its simplicity, efficiency, and ease of use for networked and concurrent applications. Below is our simple HTTP server.

package main

import (
    "log"
    "net/http"
)

func main() {
    fs := http.FileServer(http.Dir("static"))
    http.Handle("/", fs)

    log.Println("Listening on :8080...")
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal(err)
    }
}

Here's how it works: The http.FileServer(http.Dir("static")) line tells our server to serve static files from a directory named static. We then tell the server to handle all HTTP requests with the file server using http.Handle("/", fs). Finally, we start the server on port 8080 with http.ListenAndServe(":8080", nil).

Part 2: Creating the Chat Interface โœ๏ธ

Now, let's create a simple chat interface using HTML and JavaScript.

The HTML Structure ๐Ÿ†

Our HTML structure is straightforward. We have a status div to display messages to the user, a text input field for the user's message, and three buttons: one to open the WebSocket connection, one to send messages, and one to close the connection.

<!DOCTYPE html>
<html>
<head>
    <title>Chat Application</title>
    <style>
        #message-input {
            width: 400px;
        }
    </style>
</head>
<body>
    <div id="status"></div>
    <input id="message-input" type="text">
    <button id="open-button">Open Connection</button>
    <button id="send-button">Send</button>
    <button id="close-button">Close Connection</button>
    <script src="chat.js"></script>
</body>
</html>

JavaScript WebSocket Connection ๐Ÿ”ฅ

WebSockets is a protocol that allows for full-duplex communication channels over a single TCP connection. In our JavaScript code, we're going to establish a WebSocket connection with our API Gateway.

var ws;

function connect() {
    var url = 'wss://fdatgtaewatr.execute-api.AWS-REGION.amazonaws.com/prod'; // Replace with your API Gateway URL
    ws = new WebSocket(url);

    ws.onopen = function(event) {
        document.getElementById('status').innerHTML = 'Connection is established';
    };

    ws.onmessage = function(event) {
        var message = JSON.parse(event.data);
        if (message.message) {
            document.getElementById('status').innerHTML = 'Message received: ' + message.message;
        }
    };

    ws.onerror = function(event) {
        console.log('WebSocket error: ', event);
    };

    ws.onclose = function() {
        document.getElementById('status').innerHTML = 'Connection is closed';
        ws = null;
    };
}

We have four WebSocket events:

  1. onopen triggers when the connection is established.

  2. onmessage triggers when a message is received.

  3. onerror triggers when an error occurs.

  4. onclose triggers when the connection is closed.

We also have button click events to open the connection, send the message, and close the connection:

document.getElementById('open-button').addEventListener('click', function() {
    if(ws == null) {
        connect();
    } else {
        document.getElementById('status').innerHTML = 'Connection is already open';
    }
});

document.getElementById('send-button').addEventListener('click', function() {
    if(ws != null) {
        var messageInput = document.getElementById('message-input'),
            message = messageInput.value;
        ws.send(JSON.stringify({
            action: 'sendMessage',
            data: message
        }));
        messageInput.value = '';
        document.getElementById('status').innerHTML = 'Message sent: ' + message;
    } else {
        document.getElementById('status').innerHTML = 'Connection is closed. Open the connection first to send a message';
    }
});

document.getElementById('close-button').addEventListener('click', function() {
    if(ws != null) {
        ws.close();
        document.getElementById('status').innerHTML = 'Connection closed by user';
    } else {
        document.getElementById('status').innerHTML = 'Connection is already closed';
    }
});

At this point, we have a working HTML chat interface that can connect to a WebSocket, send and receive messages, and close the connection.

Part 3: Setting up API Gateway and AWS Lambda ๐Ÿ“ฆ

To make our chat application work in a real-world setting, we need to set up a backend server. In our case, we're going to use API Gateway and AWS Lambda.

Creating the API Gateway ๐Ÿ›๏ธ

AWS API Gateway allows you to create, publish, maintain, monitor, and secure APIs. For our chat application, we need to create a WebSocket API.

  1. Go to the AWS Management Console and open the API Gateway console.

  2. Choose 'Create API'.

  3. Under WebSocket, choose 'Build'.

  4. For the 'Route selection expression', enter $request.body.action.

  5. Choose 'Create API'.

Next, we need to create three routes: $connect, $disconnect, and $default.

  1. On the 'Routes' pane, choose 'Create'.

  2. For the 'Route key', enter $connect and choose 'Create'.

  3. Repeat the process for $disconnect and $default.

These routes will trigger different AWS Lambda functions depending on the action that triggered them.

Creating the AWS Lambda Functions ๐Ÿ’ก

AWS Lambda lets you run code without provisioning or managing servers. We're going to create three separate Lambda functions for our $connect, $disconnect, and $default routes.

After creating your Lambda functions (we'll leave the implementation details as an exercise for the reader), you need to integrate them with your API Gateway:

  1. In the API Gateway console, select your API.

  2. In the 'Routes' pane, select a route.

  3. Under 'Route overview', choose 'Attach integration'.

  4. In the 'Integration pane', select 'Lambda Function'.

  5. Enter the name of the corresponding Lambda function and choose 'Attach'.

Deploying the API ๐Ÿƒโ€โ™‚๏ธ

Finally, you need to deploy your API:

  1. In the API Gateway console, select your API.

  2. Choose 'Deploy API'.

  3. For 'Deployment stage', choose '[New Stage]'.

  4. For 'Stage name', enter 'prod'.

  5. Choose 'Deploy'.

After the deployment, you'll get an Invoke URL. Replace the URL in your JavaScript code with this URL.

Congratulations! You've just built a simple real-time chat application using a Golang server, AWS API Gateway, and AWS Lambda. This tutorial showed you the basics, but you can extend this application with additional features like user authentication, message history, or even group chat.

That's it for now.

You can Buy Me a Coffee if you want to and please don't forget to follow me on YouTube, Twitter, and LinkedIn also.

If you have any questions or would like to share your own experiences, feel free to leave a comment below. I'm here to support and engage with you.

Happy coding!

ย