# Post Hooks

> Run your TypeScript after any API of API Maker - reshape the answer in g.res.output, notify, log, or replace it - at the level of an instance, a database, a table, one API or a custom, system or third party API, in the reverse order of the pre hooks.

Source: https://docs.apimaker.dev/v1/docs/apis-all/hooks/postHook-api.html

A post hook is a TypeScript function of yours which runs **after** an API, with its result in `g.res.output`. It can reshape the answer, add to it, replace it, send something somewhere, or refuse to answer at all. It lives at the same levels as a [pre hook](https://docs.apimaker.dev/v1/docs/apis-all/hooks/preHook-api.html) : instance, database, table, API, custom, system and third party API, with optional groups.

> Diagram : Hooks around an API : pre hooks from the instance down to the API, the API, then post hooks from the API up to the instance
>
> Order of the hooks around a database API : the pre hooks of the instance, then of the database, then of the table, then of the API run before it ; the API runs ; the post hooks run in the opposite order, API, table, database, instance. A pre hook which returns a value answers the caller and stops everything after it.

## Order

Post hooks run from the narrowest level to the widest : the hooks of the API, then of the table, then of the database, then of the instance, each list top to bottom. The last one to run sees what every other one did.

## The code

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

async function main(g: T.IAMGlobal) {
    if (!g.req.isApiRequestFromUser) return;

    // g.res.output : the answer of the API (data), changeable
    // g.res.statusCode, g.res.warnings, g.res.errors
    // g.req : the request as the API saw it, after the pre hooks
    // g.shared : what the pre hooks left for you
}
module.exports = main;
```

| The hook | Effect |
|---|---|
| returns nothing | The next post hook runs, then the answer is sent. |
| changes `g.res.output` | The answer is what is in `g.res.output` at the end. |
| returns a value | That value becomes the answer and the remaining post hooks do not run. |
| throws | The request answers with the error, whatever the API did. The database change of the API is **not** rolled back by that. |

- Hooks run in the sandbox (or on the native process with `runOnNativeProcess`), within the sandbox timeout of the call.
- No hook runs when the answer comes from the [cache](https://docs.apimaker.dev/v1/docs/features/automatic-caching.html). Calls from your own code skip hooks unless `skipHookRunning: false`.

## Reshape the answer

**Hide a field and add a computed one**

```typescript
async function main(g: T.IAMGlobal) {
    if (!g.req.isApiRequestFromUser) return;
    const rows = Array.isArray(g.res.output) ? g.res.output : [ g.res.output ];
    for (const row of rows) {
        if (!row) continue;
        delete row.internal_notes;
        row.total_with_tax = Math.round(row.total * 1.18);
    }
}
module.exports = main;
```

## Do something after a write

**Audit row and an event after a save**

```typescript
async function main(g: T.IAMGlobal) {
    if (!g.req.isApiRequestFromUser) return;
    await g.sys.db.saveSingleOrMultiple({
        instance: 'mongodb', database: 'shop', collection: 'audit_logs',
        saveData: { api: g.req.reqInfo.apiInfo.id, by: g.req.auth.authAMDB?.email, at: new Date(), took: Date.now() - g.shared.startedAt },
    });
    await g.sys.system.emitEvent('order-placed', g.res.output);
}
module.exports = main;
```

- For a notification to WebSocket clients, nothing to write : subscribers of the table are notified after every successful call. Emit a custom WebSocket event with `g.sys.system.emitEventWS` when the notification needs another shape.
- For an event after an API hit without code, attach the event to the API on the [Events](https://docs.apimaker.dev/v1/docs/apis-all/events/user-created-events-api.html) page (automatic trigger).

## Streams

On get all by stream and query by stream the rows are already on their way when the post hooks run : `g.res.output` only holds the last chunk, and a value returned or thrown can not change what was sent. Reshape a stream in a custom API around `g.sys.db.getAllByStream` instead.

## Good to know

- WebSocket notifications and automatic events run after the post hooks, on the final answer. A post hook which throws still lets the notification go out ; a pre hook which throws does not.
- Post hooks have versions and a Test button on the API testing page, like pre hooks, and deploy with Git.

- [← Pre hooks](https://docs.apimaker.dev/v1/docs/apis-all/hooks/preHook-api.html)
- [Events](https://docs.apimaker.dev/v1/docs/apis-all/events/user-created-events-api.html)
