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.
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.
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.
When you add this WebSocket Trigger you get three output ports in the node called req, message 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 req, msg 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.
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.
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.