API Maker

The framework for AI era

Your code

WebSockets

Live updates for web and mobile apps: subscribe to any API or your own events, get only what matches.

Apps connect over WebSocket with their tokens and register for an API, a table or a custom event, with a condition on the response. After each successful call, API Maker pushes the data to the registrations that match, on whichever server holds the socket. No polling, and no socket server to build.

See it live

Step through it, slow it down, or open the full canvas.

api-maker/features/websocketsLive
  • Request
  • WebSocket
  • Redis pub/sub
  • Refused
Sockets
0connected
Registrations
0with an eventId
Pushed
0notifications
Refused
0token or rule

Apps connect with their tokens. Web and mobile apps open wss:// with the API user token and the user token. Caddy ends TLS, API Maker checks the tokens and answers CONNECTED, or closes the socket.

How it works

  1. Apps connect with their tokens.

    Web and mobile apps open wss:// with the API user token and the user token. Caddy ends TLS, API Maker checks the tokens and answers CONNECTED, or closes the socket.

  2. Register for an API, a table and a condition.

    Each registration is checked: the WebSocket event of that API, the groups of the API user, the auth provider. It gets an eventId.

  3. Only matching sockets get the data.

    After a successful call, the response goes to the caller first. Then the registrations whose criteria match it get a NOTIFICATION with the fields they selected.

  4. One socket, a whole cluster.

    Registrations live in Redis. The process that ran the API publishes the notification, the one that holds the socket pushes it, on any server.

  5. Push your own events from code.

    Create a custom WebSocket event, and emit it from a custom API, a hook, a listener or a scheduler with g.sys.system.emitEventWS. It returns how many sockets it reached.

  6. Your code decides who may listen.

    The can user connect code of a WebSocket event refuses a registration with your own message. Apps stop listening with UNREGISTER and the eventId.

What you get

Subscribe to any API

Register for the generated and schema APIs of a table, a custom API, a system API or a custom WebSocket event. Each registration gets an eventId.

Only what matters

A condition on the response picks the calls that notify a socket, and select picks the fields it receives. A kitchen screen gets the orders of its own store only.

Your own events

Create a custom WebSocket event and push it from any code with g.sys.system.emitEventWS(name, data), or from another system with the emit-event-ws system API. It returns how many sockets it reached.

Your access rules

A WebSocket event accepts the tokens of its auth providers and follows the groups of the API user. Its can user connect code refuses a registration with your own message.

Built for clusters

Registrations are kept in Redis. The process that runs the API publishes the notification, the one that holds the socket pushes it, on any server.

wss through Caddy

API Maker listens for WebSockets on port 38245. The installer puts it behind Caddy, which serves it as wss with certificates it renews by itself.

An example

A live kitchen screen

When an order is saved, the screen of its store shows it at once, the other stores see nothing, and the manager app gets "order ready" as soon as the kitchen marks it. Nothing polls, and the web servers can be many.

A kitchen screen: connect, register, listenkitchen-screen.js
const ws = new WebSocket('wss://ws.example.com/'    + '?x-am-authorization=' + encodeURIComponent(apiUserToken)    + '&x-am-user-authorization=' + encodeURIComponent(userToken));ws.onmessage = e => {    const msg = JSON.parse(e.data);    if (msg.type === 'CONNECTED') {        ws.send(JSON.stringify({            objType: 'REGISTER',            onEvents: [{                eventType: 'INSTANCES',                apiName: 'SCHEMA_POST_BULK_INSERT',                instance: 'shop', database: 'main', collection: 'orders',                condition: { conditionType: 'RESPONSE', criteria: { store_id: 7 } },                select: { _id: 1, items: 1 },                getEventData: true,            }],        }));    }    if (msg.type === 'REGISTER') console.log(msg.response.invalidOnEvents); // refused ones, with their errors    if (msg.type === 'NOTIFICATION') showOrder(msg.response.eventData);};

First add the WebSocket event of this API in API Info → WebSocket Events: registering for an API without one is refused.

Push your own event from a custom APIorder-ready.ts
import * as T from 'types';async function main(g: T.IAMGlobal) {    const { store_id, order_id } = g.req.body;    // Reaches the sockets registered for order-ready with criteria { store_id }    const reached = await g.sys.system.emitEventWS('order-ready', { store_id, order_id });    return { reached };}module.exports = main;

The app registers with eventType: 'CUSTOM_WS_EVENTS' and apiName: 'order-ready'. Delivery is an exact match on the criteria: to reach several users, register them with the same criteria or emit once per user.

Watch it

WebSocket notifications in one minute

Open the video page

Good to know

  • A condition compares flat values of the response: nested objects are not supported in its criteria.
  • No notification is sent for a call that fails before its response, for example when a pre hook throws.
  • Stream APIs can not be registered for notifications.

Questions

Do WebSocket notifications slow down my APIs?

No. The response is written first. Events, WebSocket notifications and logs run after it.

Can a client connected to one server get notifications from another server?

Yes. Registrations are kept in Redis, so the server that runs the API finds the sockets to notify, wherever they are connected.

Are calls made from my code notified too?

Yes. A save made by a custom API, a hook or a listener notifies the sockets just like a call from an app.

How does an app stop listening?

It sends { objType: 'UNREGISTER', onEvents: [eventId] } with the eventIds it got, or closes the socket.