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

Events and listeners

An event is a name with listeners : your functions that run when the event is emitted. Each listener is written in its own language. Your code emits the event, a client emits it through a system API, or API Maker emits it after a successful call of an API you attach it to. So the side effects of an operation (mails, stock, exports) stay out of the API itself.

In short

  • An event is a name with listeners : your functions that run when the event happens.
  • It happens when your code emits it, or by itself after an API you attach it to : an order saved.
  • Use it for what comes after : an email, a stock update, an export. The API that saved the order stays simple.
Events : the event orders/placed on the left, its listeners on the right
  1. The events, in folders.
  2. Its listeners : the code that runs when the event happens.
Events : emitted by code, by the system API or after an API hit, with N listeners, and no infinite loops who emits An API hit automatic trigger, after the API Your code g.sys.system.emitEvent(…) System API POST …/emit-event Event order-placed eventData travels with it listeners · your functions send-invoice g.req.eventData · timeout in minutes update-stock runs in the sandbox notify-warehouse emits another event warehouse-sync another event, its own listeners back to an event already running : stopped, no infinite loop The emitter gets the output of every listener back : { listenerName, output } Listeners run one after the other, with the log profile recording each run.
Event-Driven APIs: React to Saves, Code and HTTP Calls9 chapters and the transcript

Create an event

APIs & Logic → Events, then +.

Field Meaning
Name The name used to emit it. Unique in the account.
Automatically trigger on API hit On : the event is emitted after each successful call of the APIs listed under it. See Trigger on API hit.
Trigger on APIs APIs of tables, custom APIs, system APIs. Several at once.
Listeners One or more, each with a name, a code and its language, a timeout in minutes, versions, and runOnNativeProcess.
Save response in log Keep what the listeners returned in the log table.

Trigger on API hit

Turn on Automatically trigger on API hit, then add the APIs under Trigger on APIs. After each successful call of one of them, API Maker emits the event in the background : the caller does not wait for the listeners.

  • The event data (g.req.eventData) is the whole answer of that call : { success, statusCode, data, … }.
  • A call that failed emits nothing. A call failed when its status is 400 or more, or when its answer says "success": false.
  • A call refused before the API ran (no token, no access) emits nothing either.
  • A call from your code (g.sys.db…) emits it too, like a call over HTTP.

A listener

import * as T from 'types';
import * as db from 'db-interfaces';

async function main(g: T.IAMGlobal) {
    const data = g.req.eventData;   // what was emitted
    await g.sys.db.saveSingleOrMultiple({
        instance: 'mongodb', database: 'shop', collection: 'notifications',
        saveData: { order_no: data.order_no, text: `Order ${data.order_no} placed` },
    });
    return { notified: true };      // returned to the emitter
}
module.exports = main;
from am import G


async def main(g: G):
    data = g.req.event_data   # what was emitted
    await g.sys.db.save_single_or_multiple(
        'mongodb', 'shop', 'notifications',
        save_data={'order_no': data.order_no, 'text': f'Order {data.order_no} placed'},
    )
    return {'notified': True}  # returned to the emitter
<?php
function main(AmG $g)
{
    $data = $g->req->eventData;   // what was emitted
    $g->sys->db->saveSingleOrMultiple(
        'mongodb', 'shop', 'notifications',
        saveData: ['order_no' => $data->order_no, 'text' => "Order {$data->order_no} placed"],
    );
    return ['notified' => true];  // returned to the emitter
}
1
2
3
4
5
6
7
8
9
file static class Api
{
    public static async Task<object?> Main(G g)
    {
        var orderNo = g.Req.EventData?["order_no"]?.GetValue<long>();   // what was emitted
        await g.Sys.Db.SaveSingleOrMultiple(new Save("mongodb", "shop", "notifications", new { order_no = orderNo, text = $"Order {orderNo} placed" }));
        return new { notified = true };  // returned to the emitter
    }
}
package code

import (
    "fmt"

    "apimaker/am"
)

func Main(g *am.G) (any, error) {
    data := am.AsM(g.Req.EventData()) // what was emitted
    _, err := g.Sys.Db.SaveSingleOrMultiple(am.Save{
        Instance: "mongodb", Database: "shop", Collection: "notifications",
        SaveData: am.M{"order_no": data.Get("order_no"), "text": fmt.Sprintf("Order %v placed", data.Get("order_no"))},
    })
    if err != nil {
        return nil, err
    }
    return am.M{"notified": true}, nil // returned to the emitter
}
use am::{Errors, G, Save, Value, json};

pub fn main(g: &mut G) -> Result<Value, Errors> {
    let order_no = g.req.event_data()["order_no"].clone(); // what was emitted
    g.sys.db.save_single_or_multiple(Save::new(
        "mongodb",
        "shop",
        "notifications",
        json!({ "order_no": order_no, "text": format!("Order {order_no} placed") }),
    ))?;
    Ok(json!({ "notified": true })) // returned to the emitter
}
import am.G;
import am.JsonObject;
import am.Save;
import java.util.Map;

class Api {
    static Object main(G g) {
        JsonObject data = JsonObject.from(g.req.getEventData());   // what was emitted
        g.sys.db.saveSingleOrMultiple(new Save("mongodb", "shop", "notifications",
            Map.of("order_no", data.getLong("order_no"), "text", "Order " + data.getLong("order_no") + " placed")));
        return Map.of("notified", true);  // returned to the emitter
    }
}
import am.G
import am.JsonObject
import am.Save

fun main(g: G): Any? {
    val data = JsonObject.from(g.req.eventData)   // what was emitted
    g.sys.db.saveSingleOrMultiple(
        Save("mongodb", "shop", "notifications", mapOf("order_no" to data.getLong("order_no"), "text" to "Order ${data.getLong("order_no")} placed")),
    )
    return mapOf("notified" to true)  // returned to the emitter
}
  • Every listener gets the same g as a custom API : g.req.eventData holds the emitted data, g.sys reaches every API, g.logger writes to the log table.
  • Listeners run one after the other, each within its timeout : in the sandbox and with runOnNativeProcess alike. Empty : sandboxReqTimeout of the configuration (13 seconds), whatever time the request which emitted the event has.

Emit from your code

1
2
3
4
const result = await g.sys.system.emitEvent('order-placed', { order_no: 1001, customer_id: 42 });
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

await g.sys.system.emitEvent('order-placed', { order_no: 1001 }, [ 'send-invoice' ]);   // only these listeners
1
2
3
4
result = await g.sys.system.emit_event('order-placed', {'order_no': 1001, 'customer_id': 42})
# result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

await g.sys.system.emit_event('order-placed', {'order_no': 1001}, ['send-invoice'])  # only these listeners
1
2
3
4
$result = $g->sys->system->emitEvent('order-placed', ['order_no' => 1001, 'customer_id' => 42]);
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

$g->sys->system->emitEvent('order-placed', ['order_no' => 1001], ['send-invoice']); // only these listeners
1
2
3
4
var result = await g.Sys.System.EmitEvent("order-placed", new JsonObject { ["order_no"] = 1001, ["customer_id"] = 42 });
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

await g.Sys.System.EmitEvent("order-placed", new JsonObject { ["order_no"] = 1001 }, ["send-invoice"]); // only these listeners
1
2
3
4
5
6
7
8
9
result, err := g.Sys.System.EmitEvent("order-placed", am.M{"order_no": 1001, "customer_id": 42})
if err != nil {
    return nil, err
}
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

if _, err := g.Sys.System.EmitEvent("order-placed", am.M{"order_no": 1001}, "send-invoice"); err != nil { // only these listeners
    return nil, err
}
1
2
3
4
let result = g.sys.system.emit_event("order-placed", json!({ "order_no": 1001, "customer_id": 42 }), &[])?;
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

g.sys.system.emit_event("order-placed", json!({ "order_no": 1001 }), &["send-invoice"])?; // only these listeners
1
2
3
4
var result = g.sys.system.emitEvent("order-placed", Map.of("order_no", 1001, "customer_id", 42));
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

g.sys.system.emitEvent("order-placed", Map.of("order_no", 1001), List.of("send-invoice")); // only these listeners
1
2
3
4
val result = g.sys.system.emitEvent("order-placed", mapOf("order_no" to 1001, "customer_id" to 42))
// result.outputArr : [ { listenerName: 'send-invoice', output: { notified: true } }, … ]

g.sys.system.emitEvent("order-placed", mapOf("order_no" to 1001), listOf("send-invoice")) // only these listeners
  • The third argument names the listeners to run ; absent, every listener runs.
  • The emitter gets the output of each listener back, so an event can also be a way to run several pieces of code and collect their results.

Emit over HTTP

POST /api/system-api/admin/emit-event
{ "name": "order-placed", "eventData": { "order_no": 1001 }, "executeListeners": [ "send-invoice" ] }
  • An array of such objects emits several events in one call. See Emit event.

Chains without loops

A listener may emit another event, which may emit another. API Maker keeps the list of the events already running for the request : an emit which comes back to one of them is stopped, so a chain never becomes an infinite loop. Each level of depth is allowed ; only the cycle is refused.

Where it fits

Need Use
Something must happen after an API, in code An event with an automatic trigger, or a post hook when it must change the answer
A client must be told live A WebSocket event, emitted with emitEventWS
Something must happen on a schedule A scheduler

Good to know

  • Events and their listeners are files in Git and deploy with a pull. Listeners have versions.
  • Test a listener from the API testing page : pick the event, the listener, put the event data in the body and send.
  • On the Events page, Move up and Move down order the listeners, Grid View lists every listener of every event, Clone copies an event with its listeners and Copy full event name gives the name to emit.
  • On the native process, console.log is not captured : use g.logger.
  • A log profile records each listener run.
  • To emit over HTTP, the API user needs a group that grants the system API EMIT_EVENT. A group does not name single events.