Get a Serverless WebSocket Application Up and Running with Lolo in 5 Minutes

Written by lolocode | Published 2022/09/06
Tech Story Tags: programming | software-development | javascript | nodejs | cloud-computing | serverless | tutorial | web-development

TLDRCreate a serverless websocket application in just a few minutes. We’ll demonstrate something very simple here by using a pre-made Websocket Trigger to connect and broadcast all messages to all connected clients. You'll deploy it quickly via to the cloud for free and can use it in production right away. It goes without saying that you can connect it to a frontend so you have a chat application up and running for as many users as you'd like. Although there is a limit to a few thousand events per second. If you have more than that add replicas in your Lolo app. via the TL;DR App

WebSockets are mainly used to create real-time applications, such as chat apps, collaboration platforms and streaming dashboards. These applications take advantage of two-way/bidirectional communication between a server and users’ browsers.

Serverless is usually associated with FaaS, an isolated stateless function that is triggered by another service. Creating a Serverless Websocket application would require you to use AWS Lambda with WebSockets in API Gateway and DynamoDB to store connections. This is overly complicated.

We’ll show you how to create something faster. Serverless iFaaS platforms don’t require initialization before the function executes so you don’t pay a price for waking up a container once it has been deployed. It’s technically always live. You also have access to a baked-in key/value store which means that you don’t end up losing state between subsequent invocations. I.e. you don’t have to start from a blank slate on every invocation. Hence the lack of need for an external database such as DynamoDB.

The Plan

We’ll demonstrate something very simple here by using a pre-made Websocket Trigger to connect and broadcast all messages to all connected clients. See the visual workflow below. You’ll need a free Lolo account to set this up yourself.

If you want to get started right away you can simply copy the contents of this documentand paste it into a new application in a blank graph in Lolo. Save and Run the application to deploy it. That’s 1 minute of work.

Do follow along though if you want to understand how to use it or if you want to set this up from scratch.

Create an Application and add a Trigger

To begin, we need to set up a new WebSocket server that can handle inbound WebSocket requests from clients.

We do this by first creating a new application in your Lolo account and then adding the Lolo/Websocket Trigger from the Functions Gallery on the left of the graph. Add in a path name, such as /socket. You can also rename it.

Process the Incoming Request

When you add this WebSocket Trigger you get three output ports in the node called reqmessage and close so you need to set up ways to process these requests. Add another inline Function (i.e. a custom function) and name it ‘Process Request.’ Remove the in port and instead add in three in ports called reqmsg and close.

Link the ports to the right in and out ports with the nodes, look below for an illustration on how to do this.


We need a bit of code to do something here with the WebSocket connection. Copy and paste in the code from below. Open up the Process Request node and paste in the code below in the handler.


const connections = {};

exports.handler = async (ev, ctx) => {
  const { route, input, inputs, log, emit } = ctx;
  const { sessionId } = ev;

  // check incoming port (i.e. req, message or close)
  switch (input) {

    // on first connection
    case inputs.req:
      connections[sessionId] = {
        send: body => emit('response', { body }),
        end: () => emit('response', { end: true }),
        info: ev.headers
      }
      ev.body = { connected: true, yourConnectionId: sessionId };
      // re-route data to 'req' output port
      route(ev, 'req');
      break;

    // on subsequent messages 
    case inputs.msg:
      // re-route data to 'msg' output port
      route({ connections, message: ev.message, sessionId }, 'msg')
      break;

    // when client disconnects 
    case inputs.close:
      log.info("client has disconnected");
      delete connections[sessionId];
      break;
  }
};


The code above is checking what ports the incoming data is routed through, giving us a way to handle a new connection by adding it to connections. We are also setting up how we want to handle subsequent messages and a disconnecting client by using the in ports, msg and close.

This code above though is re-routing to other nodes via the route() method as well. As you can see we have two output routes here to ‘req’ and ‘msg.’ We thus need to add those two output ports as well.

Send back a response

Now we have two outports in the Process Request Function that needs to go somewhere, so we create another inline Function called Affirm Connectionand paste the code below.

All this does is signal the listener within the WebSocket Trigger to send a response to the client. We will route the req route to this node.

exports.handler = async(ev, ctx) => {
  const { emit, log } = ctx;
  // Log to the console that a client has connected
  log.info("client has connected");
  // send response to the client
  emit('response', ev);
};

Along with this we also need a way to handle subsequent messages. In this case, we said we would broadcast all messages to all clients when someone sends something. So, create another inline Function (i.e. a custom function) and call it Handle Messages. Paste in the code below, and see the comments to understand what it does.

exports.handler = async (ev, ctx) => {
  // extract connections, and current session id from the event data
  const { connections, sessionId } = ev;
  // send messages
  await broadcastToAll(ctx, connections, sessionId, ev);
};

const broadcastToAll = async ({ log }, connections, currentSessionId, ev) => {

  // loop through connections
  Object.keys(connections).forEach(sessionId => {
    try {
        // send message to everyone but the current sessionId
        if (currentSessionId !== sessionId) {
        connections[sessionId].send(`${sessionId} says: ${ev.message}`)
        } 
    } catch (e) {
      log.error(e)
    }
  });
};

You can remove the out ports for both of these new Functions. Now we also have to route the data visually from the Process Request node to the other two nodes we’ve created.

See these steps in action below.

Save and Deploy

Alright, that was it. You can save and deploy.

Look in the Logs for the ‘Listening to port 4000’ message to see if it is ready to use. This may take a while. We are not using any NPM modules here so should be faster but if we did we would have to wait about a minute.

Now go into the WebSocket Trigger to collect the External URL. We are going to use it to connect to this Websocket.

Open two terminals on your computer and then connect via wscat on both.

wscat -c wss://eu-1.lolo.co/:appId/socket

Remember to NPM install wscat -g (via your terminal) first if you don’t already have wscat installed.

Now you should be able to talk via the different terminals. Say hi and what’s up in either terminal and see the response broadcasted to all connected clients.

You can ask a friend to connect on a different computer and talk that way. It goes without saying that you can connect this via a frontend to build a quick chat app.

Remember if you are having issues, just copy the contents of this document and paste it into an empty graph in a Lolo application to get the entire app set up for you.

Your Lolo app in free tier should be able to handle a few thousand of events per second. If you need more than that you simply add replicas when you configure your deployment to handle the increased load.

Let us know if you have any feedback. We’re keen to create better Serverless experiences.


Also published here.


Written by lolocode | Build serverless applications and reusable functions in a NodeJS environment with one-click multi-cloud deploy.
Published by HackerNoon on 2022/09/06