API Maker

The framework for AI era

Your code

Pre & Post Hooks

Run your own code before and after any API, without touching the API itself.

A pre hook runs before an API: it can check the request, change the body, or answer on its own. A post hook runs after it and can change the output. Hooks are TypeScript, work on generated, custom and system APIs, and apply on the next request after you save them.

How it works

  1. Pre hooks run first, from the outside in.

    For a database API: the pre hooks of the instance, then of the database, then of the table, then of the API itself. Several hooks at one level run from top to bottom.

  2. A pre hook can change the request.

    Read and modify g.req.body, check the user in g.req.auth, or throw an error to stop the call with a message.

  3. Returning a value answers the call.

    When a hook returns something, that becomes the response and the API does not run. A plain return only leaves that hook.

  4. Then the API runs.

    The generated query or your custom code runs with the request as the hooks left it.

  5. Post hooks run from the inside out.

    API level first, then table, database and instance. They read the result and can replace it with g.res.output.

What you get

Set once, apply everywhere

A hook on an instance runs for every table in it. A hook on a table runs for all its generated and schema APIs.

Only for some groups

Give a hook group names and it runs only for API users in one of those groups, for example audit logs for partners.

Full access to g

Hooks query other tables, read secrets, call external APIs, emit events and import your utility classes.

Switch on and off

Every hook has its own active flag. Turn one off to test without it: no restart, no deployment.

Know who is calling

g.req.isApiRequestFromUser tells a real client call from an internal one, so a hook can skip calls made by your own code.

Where it runs

Hooks run in the sandbox like custom APIs, or on the native process when you allow it for a trusted hook.

An example

Rules the database cannot hold

Orders must have lines, prices must come from the product table and not from the client, and every change must record who made it. Three pre hooks on the orders table enforce it for the generated APIs, the schema APIs and every app that calls them.

Pre hook on the orders table: stamp the user, refuse empty orderspre-hook.ts
import * as T from 'types';async function main(g: T.IAMGlobal) {    if (!g.req.isApiRequestFromUser) return; // calls from your own code pass through    const order = g.req.body;    if (!order.lines?.length) throw 'An order needs at least one line.';    order.created_by = g.req.auth.authAMDB?.username;}module.exports = main;
Post hook: hide the internal cost from the answerpost-hook.ts
import * as T from 'types';async function main(g: T.IAMGlobal) {    const output = g.res.output;    const rows = Array.isArray(output) ? output : [output];    for (const row of rows) delete row.internal_cost;    g.res.output = output; // the answer the client gets}module.exports = main;

Good to know

  • Stream APIs run pre hooks only: their response is sent while it is read, so post hooks cannot change it.
  • A response served from the cache does not run the hooks.
  • Calls made from your code with g.sys.db skip hooks by default. Pass skipHookRunning: false to run them.

Questions

Which APIs can have hooks?

Every generated and schema API, at the instance, database, table or API level, and every system API. Custom APIs get hooks per version.

In which order do several hooks run?

Pre hooks: instance, database, table, API, and from top to bottom inside a level. Post hooks: the other way round, API first and instance last.

How do I stop a request with an error?

Throw in the pre hook. The call stops and the client gets your message in errors.