# Events and Listeners

> Events in API Maker - named events with any number of listeners in TypeScript, Python, PHP, C#, Go, Rust, Java or Kotlin, emitted from your code, from the emit-event system API or automatically after an API hit, with the outputs of the listeners returned and infinite chains stopped.

Source: https://docs.apimaker.dev/v4/docs/apis-all/events/user-created-events-api.html

An event is a name with listeners : functions which run when the event is emitted, each in the [language](https://docs.apimaker.dev/v4/docs/languages/index.html) it was written in. Your code emits it, a client emits it through the system API, or API Maker emits it on its own after an API you attach it to. Events keep the side effects of an operation (mails, stock, notifications, exports) out of the API which triggers them.

> Diagram : Events : emitted by code, by the system API or after an API hit, with N listeners, and no infinite loops
>
> Events in API Maker : an event is emitted by your code with emitEvent, by the emit-event system API, or automatically after an API it is attached to ; its listeners, your TypeScript functions, run in the sandbox ; a listener can emit another event, and a chain which comes back to an event already running is stopped.

## 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 every successful call of the APIs listed under it. |
| Trigger on APIs | Instance APIs (an API of a table), 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. |

## A listener

**TypeScript**

```typescript
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;
```

**Python**

```python
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**

```php
<?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
}
```

**C#**

```csharp
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
    }
}
```

**Go**

```go
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
}
```

**Rust**

```rust
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
}
```

**Java**

```java
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
    }
}
```

**Kotlin**

```kotlin
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.
- For an **automatic trigger**, `g.req.eventData` is the whole answer of the API which triggered it (`{ success, statusCode, data, … }`), so a listener sees what was saved or read.

## Emit from your code

**TypeScript**

```typescript
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
```

**Python**

```python
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
```

**PHP**

```php
$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
```

**C#**

```csharp
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
```

**Go**

```go
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
}
```

**Rust**

```rust
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
```

**Java**

```java
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
```

**Kotlin**

```kotlin
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

```text
POST /api/system-api/admin/emit-event
```

```json
{ "name": "order-placed", "eventData": { "order_no": 1001 }, "executeListeners": [ "send-invoice" ] }
```

- An array of such objects emits several events in one call. See [Emit event](https://docs.apimaker.dev/v4/docs/apis-all/system-apis/system-generated-emit-event-api.html).

## 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](https://docs.apimaker.dev/v4/docs/apis-all/hooks/postHook-api.html) when it must change the answer |
| A client must be told live | A [WebSocket event](https://docs.apimaker.dev/v4/docs/pages/web-socket-event-page.html), emitted with `emitEventWS` |
| Something must happen on a schedule | A [scheduler](https://docs.apimaker.dev/v4/docs/apis-all/schedulers/user-created-schedulers-api.html) |

## 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](https://docs.apimaker.dev/v4/docs/features/developer-tools.html#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](https://docs.apimaker.dev/v4/docs/logs/logs-and-dashboards.html#log-profiles) records each listener run.
- Groups grant events : an API user needs a group with the event to emit it through the system API.
