Serverless and WebSockets is a bit of a contradiction. Serverless implies that weâre not managing servers ourselves, theyâve been abstracted away for our convenience. WebSockets allow us to communicate between a âclientâ and a âserverâ, but what happens when that server is âserverlessâ?! How can the connection be maintained? Are we putting the server back into serverless?
What does serverless mean, actually?
It is often joked that serverless is just âsomebody elseâs serverâ. This is the premise of serverless. It is someone elseâs server, and also their maintenance, patching and provisioning cycle, operations, capacity planning and engineering.
Serverless providers allow developers to outsource the management of a server, the engineering and cost overhead and only pay for what they use. Serverless allows us to elastically scale with need, precisely because weâre not using the computing power constantly. We pay for the execution that we need and the service âspins downâ once the execution is complete. So, the question remains, if we use WebSockets to create a constant connection, will we end up paying for constant server uptime? Is this possible? Is it expensive?
What are WebSockets, actually?
The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the userâs browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply. From MDN
WebSockets enable a connection between a browser and a web server which stays open, unlike alternatives like HTTP polling. This persistent connection means that data can be transferred as it happens, in realtime. WebSockets work by establishing a TCP/IP socket connection to a WebSocket server, which remains open for the duration of the session. The client and server then have to decide on a protocol â the sequence and type of messages â to be sent and received over the connection.
Why are people confused by the idea of serverless WebSockets?
If âserverlessâ is a server maintained by someone else, which spins up and down as needed, and WebSockets are a persistent, open socket connection to a server, then it seems like âserverless WebSocketsâ is a contradiction in terms. Either the connection is going to be severed or weâll end up paying for constant compute time, which would imply that maintaining your own server might be preferable. Maintaining your own server of course comes with all of the thought and cost overhead we mentioned before
So when we discuss serverless WebSockets, what we really mean is âa service that will run a server for you, which you can pay for per-consumption, and maintain a persistent connection to, without having to worry about scalability, maintenance, uptime or patchingâ
Serverless WebSockets with Ably
We are closely watching the rising tide of serverless hosting, and we think itâs both great for the industry and great for users. At Ably, our mission is to make Realtime Edge Messaging simple and seamless - as easy as serverless technology has made app building. Ably allows developers to send millions of messages around the world instantaneously. We do this by provisioning auto-scaling, managed web socket servers that you can use in your apps!
If youâre looking to add realtime, engaging, interactive experiences, to your apps with WebSockets â be they serverless or not â Ably can help out.
Ably does all of the hard work of designing a meaningful protocol on top of the WebSockets connection â providing âchannelsâ and messaging patterns for you to use in your apps rather than just a ânakedâ WebSockets connection. In the same way that serverless services eliminate the engineering overhead of managing a server, Ably eliminates the engineering overhead of managing a WebSocket connection. Scaling provisions, geographic load balancing, managing lost connections and message ordering is all managed for you.
Now that we understand how to maintain a WebSocket connection on a Serverless Architecture, lets actually try building one. This demo will walk you through using Ably in your Netlify projects.
To use Ably, you will need an Ably API key. If you do not already have an account, you can sign up now for a free Ably account. To create an API key:
- Log into your app dashboard.
- Under âYour appsâ, click on âManage appâ for any app you wish to use for this tutorial, or create a new one with the âCreate New Appâ button.
- Click on the âAPI Keysâ tab.
- Copy the secret âAPI Keyâ value from your Root key, we will use this to configure your app.
Getting Started with Ably and Netlify
Ably has partnered with Netlify to provide an easy to use GitHub template, which will get you started using Ably in a Netlify powered site.
- Click the âDeploy to Netlifyâ button above to deploy this GitHub repo.
- When prompted, add the ABLY_API_KEY environment variable with the key you created earlier
Now you will have a Netlify hosted project which uses Ably to maintain a WebSocket connection.
If you already have a Netlify hosted application, it is just as easy to get started with Ably.
Adding Ably to your existing Netlify apps
Ably provides a JavaScript SDK (with TypeScript types), so you only need add a few lines of code to your app to get started:
1. Adding the JavaScript SDK
You will need to use the Ably JavaScript SDK in two places in your application â in the client side application, and in a Netlify serverless function created in the next step. (Netlify supports many different frontend frameworks and tools, so you may need to adjust the following for your stack). To install the Ably-JS package as a dependency run:
npm install ably --save- Add a Netlify Serverless Function to manage the Ably API key
In order to keep your Ably API key secret, to protect it from misuse, you will need to add a Netlify function to support Token Authentication. This function will be called by the client side Ably SDK instance to get a temporary, short lived token, that will be returned to the browser and used to authenticate the connection with Ably.
First, create a new environment variable within Netlify called ABLY_API_KEY and give it your new Ably API key as a value.
Next, create a new function to read that variable and call the Ably SDK. By default, Netlify expects functions to be found in
/.netlify/functions
It is possible to override this default location if necessary, so adjust as appropriate for your existing solution.
Create a new directory for the function, called ably-token-request. Inside this directory, create two new files:
ably-token-requestâââ package.jsonâââ index.tsThe package.json file looks like this:
{ "name": "ably-token-request", "version": "1.0.0", "description": "ably token request", "main": "index.ts", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "Netlify", "Serverless", "Typescript" ], "author": "Netlify", "license": "MIT", "dependencies": { "@netlify/functions": "^1.0.0", "@types/node": "^14.0.0", "typescript": "^4.7.4", "ably": "^1.2.29", "dotenv": "^16.0.1" }}This is a basic NPM package definition with a few dependencies - the @netlify/functions package for Type definitions, @types/node for Node types, TypeScript, the Ably SDK, and dotenv which will load environment variables during development.
Now, update the index.ts file with the Netlify function code:
import * as dotenv from "dotenv";import * as Ably from "ably/promises";import { HandlerEvent, HandlerContext } from "@netlify/functions";
dotenv.config();
export async function handler(event: HandlerEvent, context: HandlerContext) { if (!process.env.ABLY_API_KEY) { return { statusCode: 500, headers: { "content-type": "application/json" }, body: JSON.stringify(`Missing ABLY_API_KEY environment variable. If you're running locally, please ensure you have a ./.env file with a value for ABLY_API_KEY=your-key. If you're running in Netlify, make sure you've configured env variable ABLY_API_KEY.`) } }
const clientId = event.queryStringParameters["clientId"] || process.env.DEFAULT_CLIENT_ID || "NO_CLIENT_ID"; const client = new Ably.Rest(process.env.ABLY_API_KEY); const tokenRequestData = await client.auth.createTokenRequest({ clientId: clientId }); return { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify(tokenRequestData) };}This function, when called, loads your API key from Netlify environment variables and uses the Ably SDK to create a temporary token for your application to use. The client-side SDK takes care of refreshing this token during long-lived sessions.
3. Adding Client Side Code that uses Ably
Depending on how your application is built, your usage of Ably will be different. Below is a vanilla TypeScript example that uses Ably to create a new authenticated instance of the SDK, and subsequently subscribe to, and publish a message:
import { Types } from "ably";import * as Ably from "ably/promises";
(async () => {
const optionalClientId = "optionalClientId"; // When not provided in authUrl, a default will be used. const connection = new Ably.Realtime.Promise({ authUrl: `/.netlify/functions //ably-token-request?clientId=${optionalClientId}` }); const channel = connection.channels.get("some-channel-name");
await channel.subscribe((msg: Types.Message) => { console.log("Ably message received", msg); document.getElementById("response").innerHTML += "<br />" + JSON.stringify(msg); });
channel.publish("hello-world-message", { message: "Hello world!" });})();Itâs worth noting that when instantiating an instance of Ably.Realtime.Promise (the Ably SDK), weâre providing an AuthUrl parameter that points to the Netlify function we created in the previous step. This is all that you need to do to make sure youâre securely authenticating with Ably.
Serverless WebSocket to the World
It is our hope that this template and walkthrough will have demystified the premise of Serverless WebSockets. You are now empowered to create all of the interactive, multi user, engaging experience that WebSockets allow in your serverless environments without any of the operational overhead! Go forth and message!
