API Maker

The framework for AI era

Level 3 · ScaleArchitecture 06 of 13

Real-time events across servers

WebSocket clients on any server, changes made on any server: Redis connects them.

Dashboards, apps and devices keep a WebSocket open to whichever server they reached. An API call answered by any server is published in Redis and pushed to every matching subscriber, on every server, within milliseconds.

See it live

Every request is drawn as it travels. Slow it down, pause, go step by step, or open the full canvas and zoom in.

api-maker/architectures/realtime-websocketsLive
  • Request
  • Response
  • Write
  • WebSocket event
Servers
3/3up
Events
0pushed
WebSocket port
38245wss via the LB
Capacity
~8,277req/s

Clients subscribe once, on any server. Dashboards, driver apps and screens open a WebSocket through the load balancer. Their subscriptions are kept in Redis.

How it works

The same steps as the diagram, in more detail.

  1. Clients subscribe once, on any server.

    A frontend or a device opens a WebSocket to port 38245, through the load balancer, and stays on the server it reached. It registers the events it wants: calls of an API, like the insert of orders, with conditions on the response, or custom events. Every subscription becomes a key in the internal Redis, which is how the other servers find it.

  2. A change arrives on any server.

    Requests keep flowing through the load balancer to the least busy server. A new order may be written by server 1 while the dashboards that want to see it are connected to server 2 or 3. No server needs to know where the subscribers are.

  3. Redis delivers it to every server.

    The call is published as an event in the internal Redis. Redis delivers it to every process that registered a matching subscription, on any server, and each one pushes it down the WebSockets it holds. A dashboard connected to server 3 sees an order written by server 1 within milliseconds.

  4. Only the subscribers who asked for it.

    Subscriptions carry conditions on the response and a list of fields to send. An event only goes where a condition matches, so servers without matching subscribers receive nothing, and clients only get the fields they asked for. Access is checked when the client subscribes, with the token of the right auth provider.

  5. Your code emits events too.

    Besides database changes, custom code can emit WebSocket events of your own, like the position of a driver or the progress of a job. They travel the same way: through Redis, to every server holding a subscriber, with the same conditions and access checks.

  6. A server stops, its clients move.

    When a server stops, the WebSockets it held close. The clients reconnect through the load balancer, which sends them to a healthy server, and register their subscriptions again. Events raised anywhere reach them through their new server.

Why choose this architecture

Every subscriber, every server

An order written through server 1 reaches dashboards connected to servers 2 and 3 within milliseconds.

Only what each client asked for

Conditions on the response decide who gets an event, and select sends only the fields a client needs.

Checked like any API

Clients subscribe with a token of the right auth provider, and your code can accept or refuse each one.

Events of your own

Custom code emits custom WebSocket events, delivered with the same conditions and checks.

Survives a server loss

Clients of a stopped server reconnect to another one and subscribe again. Nothing to configure.

No extra service

The WebSocket server is part of API Maker, built on uWebSockets.js, and Redis is the one you already run.

Server configuration

Plain VPS from any provider, or your own servers, with Ubuntu 22.04 LTS. Sizes are a starting point: measure and adjust.

Load balancer

×1
CPU
2 vCPU
Memory
2 GB
Storage
20 GB SSD

Runs

  • HTTPS to port 38246 of the servers
  • Secure WebSockets (wss) to port 38245
  • An idle timeout of an hour or more

Ports

  • 443: https and wss

API servers

×3 or more
CPU
4 – 8 vCPU
Memory
8 – 16 GB
Storage
40 GB SSD

Runs

  • API Maker: HTTP APIs and the WebSocket server
  • Docker: sandboxes of custom code

Ports

  • 38246 and 38245: from the load balancer only

Many long-lived connections: raise the limit of open files (ulimit -n) of the API Maker process.

Redis server

×1
CPU
2 – 4 vCPU
Memory
8 GB
Storage
20 GB SSD

Runs

  • Redis internal: a key per subscription, pub/sub of the events
  • Redis cache: responses of your APIs

Ports

  • 6379 and 6390: from the API servers only

Data servers

2+
CPU
4 – 8 vCPU
Memory
16 – 32 GB
Storage
200 GB+ NVMe

Runs

  • API Maker DB, MongoDB replica set
  • Your databases
A client subscribes to new orders of one storesubscribe.json
{    "objType": "REGISTER",    "onEvents": [{        "eventType": "INSTANCES",        "apiName": "GEN_POST_BULK_INSERT",        "instance": "shop", "database": "public", "collection": "orders",        "condition": { "conditionType": "RESPONSE", "criteria": { "store": "42" } },        "select": { "id": 1, "items": 1, "total": 1 },        "getEventData": true    }]}

Sent over wss://api.example.com with the token of the client. Every insert of an order of store 42, through any server, is pushed to it.

Good to know

  • Every WebSocket is a long-lived connection: size the servers and the load balancer for their number, not only for requests.
  • The internal Redis carries every event: keep it close to the servers, with enough memory for the subscriptions.
  • Clients must reconnect on their own when a connection closes: every WebSocket client library can.
When you outgrow itSeveral apps or clients should share one platform, each in its own space. Many projects, one platform

Questions

Do WebSockets need sticky sessions?

No. A WebSocket is one long connection, so it stays on the server it reached by itself. Events raised on any other server reach it through Redis.

What can a client subscribe to?

Calls of the generated APIs of your tables, like the insert or the update of orders, with conditions on the response and the fields to receive. Also calls of custom APIs, system APIs and third-party APIs, and custom WebSocket events emitted by your code.

How are subscriptions secured?

A client connects with the token of an auth provider and each WebSocket event accepts the tokens of the provider chosen for it. Custom code can also decide, for each client, whether it may subscribe.