Skip to main content

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 → invokes users.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:
Function executed successfully and returned data:
Function encountered an error:
Function accepted the work but will complete later (async background job):
Function completed but has no response data:

Internal Implementation

From the engine’s perspective (Rust implementation), functions are stored in the FunctionsRegistry:
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, doThing

Keep 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