Skip to main content

Register a function

Inside a worker, worker.registerFunction(id, handler) makes a function callable from anywhere in the iii system. The id follows the service::name form; the handler receives the call’s payload and returns the result.

Call a function

call runs a function and resolves to its result. The engine routes the call to the worker that registered the function, wherever it runs. Call a function from the terminal with iii call, or from worker code with worker.call. The action field sets how the call is delivered: by default the call waits for the function to return its result, or for the configured timeout to fire.
The actions are:
  • Default (synchronous). No action set. The call waits for the function to return its result or for the configured timeout to fire.
  • CallAction.Void(). Fire-and-forget. The call returns at once; the function still runs, and the caller does not see the result.
  • CallAction.Enqueue({ queue }). Provided by queue. Routes the call through a named queue with retries; the call returns once the message is enqueued.
Workers can provide their own CallActions. Check each worker’s documentation for the actions it offers.
Refer to Using iii / Namespaces for more information on namespaces and calling functions in a specific namespace.
A call to a function that the calling worker registered itself runs in-process once the engine confirmed the registration (local dispatch), with the same result, errors, and timeout as a call through the engine. Pass dispatch: "remote" (Python "dispatch": "remote", Rust .dispatch(Dispatch::Remote), Go Dispatch: iii.DispatchRemote) to send one call through the engine. See Local dispatch.
In Python, every blocking method has an awaitable twin (call_async, shutdown_async, create_channel_async) for use inside asyncio. See the Python SDK reference.

Routing to a namespace

Workers and their associated functions can be namespaced. A function id is unique per namespace, not globally: state::get can be registered once in default, once in orders, and once in analytics. Set namespace on the call to pick one. Resolution is strict. A call that names orders resolves there and nowhere else, and a call that names none resolves in the namespace of the calling worker. A miss returns function_not_found naming the namespaces where the id does exist.
iii call reaches default unless you pass --namespace <NS>. For calling across namespaces from worker code, see Use namespaces.

Run a function when something happens

A call runs a function once, when you make it. To run a function each time something happens (another function runs, an HTTP request arrives, a cron schedule fires, a row changes), observe the function that represents it:
See Observers.

Every function is observable

Any worker can observe any function, private ones included, unless the function is registered with observable: false or RBAC restricts it. The worker that registers a function can make it private or unobservable, and can override what iii does by default when it is observed; see Observable functions.

Call functions from the CLI

iii call is a useful development tool. Pass --help to any function to see its arguments and a description of what it does:
Because iii call runs through the exact same code paths as the rest of the system, you can do real work straight from the terminal: figure out what a function does, apply database modifications, make state changes, or try any other manipulation or new code interactively. That makes it a useful tool for development and debugging.
Request and response formats are what make iii call <function> --help work. Functions can carry JSON Schemas for their request payload and response shape. For how to create these schemas when registering a function, see Creating Workers / Functions.

Common functions

A handful of functions ship with the iii engine and the standard workers. You’ll likely call them from almost every iii project. They look like any function you’d register yourself and are called the same way (with iii call or worker.call). The only thing special about them is that you didn’t have to register them.

Engine functions (engine::*)

The engine itself registers a small set of introspection and lifecycle functions. Full request and response schemas are in the engine protocol reference. The engine also hosts event sources for its registry. Observe them to react when it changes: See Engine-hosted event sources for their events.

Common workers

Each of these is published by a separate worker. Function ids, payload shapes, and per-function behaviour are in the worker’s own docs at workers.iii.dev:
iii-stream is deprecated (iii-stream) and will be removed in an upcoming release. pubsub is deprecated (pubsub) and will be removed in an upcoming release. Behavior is unchanged for now. See Migrate from iii-stream and pubsub.
  • HTTP: Serves HTTP routes. Observe http::on-request with a method and a path to add a route. See http.
  • Cron: Runs functions on a schedule. Observe cron::on-tick with an expression. See cron.
  • State: KV-style state with scoped key namespaces (distinct from routing namespaces). Each create, update, and delete runs state::on-changed, which you observe. See state.
  • Stream (deprecated): Real-time push to connected clients over WebSocket. See iii-stream. Use observable functions or channels for new work.
  • Queue: Durable, ordered job processing with retries, concurrency limits, and a dead-letter queue. Observe queue::on-message to consume a topic. See queue.
  • Pub/Sub (deprecated): Lightweight in-engine topic subscription for fan-out without durability guarantees. See pubsub. Use observable functions or durable queue topics for new work.
  • Observability: Traces, logs, metrics, alerts, sampling rules, and rollups. See iii-observability.