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.
import { init } from 'iii-sdk';

const iii = init('ws://localhost:49134');

// Register a simple function
iii.registerFunction({ id: 'math.add' }, async (input) => {
  return { sum: input.a + input.b };
});

// Register with schema validation
iii.registerFunction({
  id: 'users.create',
  description: 'Create a new user',
  request_format: {
    email: { type: 'string' },
    name: { type: 'string' }
  },
  response_format: {
    userId: { type: 'string' },
    created: { type: 'boolean' }
  }
}, async (input) => {
  const user = await db.users.create(input);
  return { userId: user.id, created: true };
});

External Functions (HTTP Invocation)

External functions invoke third-party HTTP endpoints. The engine handles the HTTP request/response lifecycle transparently.
iii.registerFunction({
  id: 'external.my_lambda',
  description: 'External Lambda function',
  invocation: {
    url: 'https://api.example.com/lambda',
    method: 'POST',
    timeout_ms: 30000,
    headers: {
      'x-api-key': process.env.LAMBDA_API_KEY
    },
    auth: {
      type: 'bearer',
      token_key: 'LAMBDA_TOKEN'
    }
  }
});
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:
// Fire-and-forget (no response needed)
await iii.call('notifications.send', {
  userId: '123',
  message: 'Hello!'
});

// Wait for response
const result = await iii.call('math.add', { a: 5, b: 3 });
console.log(result.sum); // 8

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:
return { sum: 8 };
Function encountered an error:
throw new Error('Invalid input');
// Or explicitly return error
return {
  error: {
    code: 'invalid_input',
    message: 'Input must be positive'
  }
};
Function accepted the work but will complete later (async background job):
// Start background job
startBackgroundJob(input);
return { status: 'deferred', jobId: 'abc123' };
Function completed but has no response data:
// Fire-and-forget operations
await logEvent(input);
return; // or return null

Internal Implementation

From the engine’s perspective (Rust implementation), functions are stored in the FunctionsRegistry:
pub struct Function {
    pub handler: Arc<HandlerFn>,
    pub _function_id: String,
    pub _description: Option<String>,
    pub request_format: Option<Value>,
    pub response_format: Option<Value>,
    pub metadata: Option<Value>,
}

pub enum FunctionResult<T, E> {
    Success(T),
    Failure(E),
    Deferred,
    NoResult,
}
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

Build docs developers (and LLMs) love