Skip to content
This page View Markdown Open in ChatGPT Open in Claude

WebSocket events

API Maker runs a WebSocket server (port 38245, wss:// behind Caddy). A client connects with its tokens, subscribes to what it wants to hear about, and receives a message every time a matching API succeeds on any server of the cluster, or when your code emits a custom event. No polling, no message broker of your own.

WebSockets : connect with the tokens, register with a condition, get notified from any server Your app browser · mobile new WebSocket( wss://…:38245/ ?x-am-authorization= &x-am-user-auth…=) then REGISTER, then listen 1 · CONNECTED 2 · REGISTER { onEvents } 3 · REGISTER { valid, invalid } 4 · NOTIFICATION { eventData } API Maker cluster WebSocket server checks the tokens, keeps the subscriptions with their condition Redis carries every notification to every server, so the client can be anywhere what sends a notification A successful API call table, custom, system, third party Your code emitEventWS(name, data) or POST …/emit-event-ws The condition { conditionType: 'RESPONSE', criteria: { customer_id: 42 } } Only answers matching the criteria reach this client ; select picks the fields, getEventData sends them Any server of the cluster can serve the socket and any server can trigger the notification.

1. Connect

The tokens go in the query string of the WebSocket URL, with the same names as the request headers.

1
2
3
4
5
const ws = new WebSocket(
    'wss://ws.example.com/'                                  // the WebSocket URL of your API Maker
    + '?x-am-authorization=' + encodeURIComponent(apiUserToken)
    + '&x-am-user-authorization=' + encodeURIComponent(personToken)   // when your events need the person
);
  • x-am-authorization : the API user, always. The person token of the auth provider of the events you subscribe to : x-am-user-authorization, x-google-authorization, x-azure-authorization, x-aws-authorization or x-custom-authorization. API Maker finds out which provider a token belongs to ; &authProviders=name1,name2 narrows it when you want.
  • The first message from the server is CONNECTED :
{ "type": "CONNECTED", "response": { "connected": true } }
  • A bad token closes the connection with a TOKEN_VALIDATION message which says which header is wrong.

2. Subscribe

Send a REGISTER message with the events you want, then read the answer : the ones accepted, with their eventId, and the ones refused with the reason.

ws.onmessage = (e) => {
    const msg = JSON.parse(e.data);
    if (msg.type === 'CONNECTED') {
        ws.send(JSON.stringify({
            objType: 'REGISTER',
            onEvents: [
                {   // every successful get all on this table
                    eventType: 'INSTANCES', apiName: 'SCHEMA_GET_ALL',
                    instance: 'mongodb', database: 'shop', collection: 'orders',
                    getEventData: true, select: { order_no: 1, status: 1 },
                },
                {   // a custom event, only when the criteria matches the emitted data
                    eventType: 'CUSTOM_WS_EVENTS', apiName: 'default/orders-updated',
                    condition: { conditionType: 'RESPONSE', criteria: { recipientId: myUserId } },
                    getEventData: true,
                },
            ],
        }));
    } else if (msg.type === 'REGISTER') {
        console.log(msg.response.validOnEvents, msg.response.invalidOnEvents);   // eventId per subscription
    } else if (msg.type === 'NOTIFICATION') {
        render(msg.response.eventId, msg.response.eventData);
    }
};
Key of an event Meaning
eventType INSTANCES, CUSTOM_APIS, SYSTEM_APIS, THIRD_PARTY_APIS or CUSTOM_WS_EVENTS.
apiName The API id for a table (SCHEMA_GET_ALL, GEN_POST_BULK_INSERT…), the name of the custom, system or third party API, or the name of the custom WebSocket event.
instance, database, collection or table The table, for INSTANCES.
apiBundleName, apiVersion The bundle and version, for THIRD_PARTY_APIS.
condition { conditionType: 'RESPONSE', criteria: { field: value } } : only the answers whose data has these values reach this client. For a custom event, the emitted data must match the criteria exactly, which is how one notification reaches one person.
select The fields of the data to send, { field: 1 }.
getEventData true sends the data ; false sends only the fact that it happened.
  • Subscriptions are checked against the groups of the API user and the auth provider of the event : invalidOnEvents says why one was refused.

3. Receive

{ "type": "NOTIFICATION", "response": { "eventId": "…", "eventType": "INSTANCES", "eventData": [ { "order_no": 1001, "status": "PAID" } ] } }
  • Notifications come from any server of the cluster : Redis passes them between the servers.
  • Unsubscribe with the eventId of the register answer :
ws.send(JSON.stringify({ objType: 'UNREGISTER', onEvents: [ eventId ] }));

When a notification is sent

  • After every successful call of a subscribed API : the answer of the call is the data, filtered by condition and select. The call can come over HTTP or from your code (custom APIs, hooks, events…).
  • Not when an error happens before the answer exists : a pre hook which throws sends nothing. A post hook which throws does not stop the notification.
  • For a custom WebSocket event, when your code emits it :
await g.sys.system.emitEventWS('default/orders-updated', { recipientId: '42', type: 'order-updated', order_no: 1001 });
POST /api/system-api/admin/emit-event-ws
{ "name": "default/orders-updated", "eventData": { "recipientId": "42", "order_no": 1001 } }
  • To notify N people, emit once per person with their id in the data ; each client subscribes with its id in criteria.

Custom WebSocket events

API Info → WebSocket Events lists the event names of the account. A custom event needs to exist there before it can be subscribed to or emitted.

Field Meaning
Name The apiName of the subscription and the name given to emitEventWS.
Auth providers The provider whose token a client must have validated to subscribe. Empty : the first auth provider of the account.
Can user connect code A function which decides whether a client may subscribe : return { canConnect: true } or { canConnect: false, errorText: '…' }. It sees the tokens in g.req.auth.
Trigger on API Optional : an API whose success also emits this event.

From your code

  • g.sys.system.emitEventWS(name, data) from any custom API, hook, event, scheduler or migration.
  • The sample custom API /default/ws-notify of a new account has the complete client snippet in its comments.
  • The system API does the same over HTTP.

Good to know

  • The admin panel and the local client use the same server with their own tokens.
  • Groups grant WebSocket events : an API user subscribes to a custom event only when a group allows it.
  • Behind Caddy, the install script gives the WebSocket its own host or port : the install prints the WebSocket URL (BE_WS_HOST_PORT).