What is a Function?
A Function is the core primitive in iii that represents any unit of work. Functions are language-agnostic, async-capable, and can exist anywhere - locally, in the cloud, on serverless platforms, or even as third-party HTTP endpoints.All functionality in your backend deconstructs into functions. They can mutate state, invoke other functions, modify databases, and do anything a typical function can do.
Key Characteristics
- Universal: Works across any runtime (Node.js, Python, Rust, etc.)
- Async-first: Built on async/await patterns for non-blocking execution
- Location-independent: Can run locally or as remote HTTP endpoints
- Type-safe: Optional request/response format schemas for validation
- Observable: Built-in distributed tracing with OpenTelemetry
Function Structure
Every function in iii has:- ID: A unique identifier (e.g.,
math.add,users.create) - Handler: The actual code that executes when invoked
- Input: JSON data passed to the function
- Output: Optional JSON response returned by the function
- Metadata: Optional schema definitions and descriptive information
Registering Functions
Local Functions
Local functions run in your worker process. They’re registered via the SDK and execute directly in your runtime.External Functions (HTTP Invocation)
External functions invoke third-party HTTP endpoints. The engine handles the HTTP request/response lifecycle transparently.External functions are invoked via HTTP but appear identical to local functions from a caller’s perspective. The engine manages authentication, retries, and error handling.
Invoking Functions
From Code
Functions can invoke other functions directly using the SDK:From Triggers
Functions are typically invoked automatically by Triggers. For example:- HTTP trigger:
POST /users→ invokesusers.create - Cron trigger: every hour → invokes
reports.generate - Queue trigger: message arrives → invokes
jobs.process
Function Lifecycle
The function lifecycle in iii follows this flow:Function Results
Functions can return different result types:Success
Success
Function executed successfully and returned data:
Failure
Failure
Function encountered an error:
Deferred
Deferred
Function accepted the work but will complete later (async background job):
NoResult
NoResult
Function completed but has no response data:
Internal Implementation
From the engine’s perspective (Rust implementation), functions are stored in theFunctionsRegistry:
The engine maintains a
FunctionsRegistry (a thread-safe DashMap) that tracks all registered functions across all connected workers.Best Practices
Use descriptive IDs
Name functions with clear namespaces:
users.create, orders.process, not func1, doThingKeep functions focused
Each function should do one thing well. Compose complex workflows from simple functions.
Handle errors gracefully
Return structured error codes and messages. Avoid throwing generic errors.
Add schemas
Define request/response formats for validation and documentation.
Next Steps
Triggers
Learn how to automatically invoke functions with triggers
Discovery
Understand how functions are discovered across workers