Skip to main content

What is a Trigger?

A Trigger is what causes a Function to run. Triggers connect event sources to your functions, enabling automatic execution in response to:
  • HTTP requests
  • Scheduled cron jobs
  • Queue/pubsub messages
  • Stream events
  • Custom event sources
Triggers decouple your business logic (functions) from how they’re invoked. The same function can be triggered by HTTP, cron, or a queue without any code changes.

Trigger Architecture

Triggers have a two-level registration system:
  1. Trigger Types: Define the kind of event source (e.g., http, cron, queue)
  2. Triggers: Specific instances that connect a trigger type to a function

Built-in Trigger Types

HTTP Triggers

Map HTTP routes to functions. Powered by the RestApiModule.
iii.registerTrigger({
  type: 'http',
  function_id: 'users.create',
  config: {
    api_path: 'users',
    http_method: 'POST'
  }
});

// With path parameters
iii.registerTrigger({
  type: 'http',
  function_id: 'users.get',
  config: {
    api_path: 'users/:id',
    http_method: 'GET'
  }
});
HTTP triggers automatically parse request bodies, query parameters, and path params into the function input.

Cron Triggers

Schedule functions to run on a recurring basis using cron expressions.
iii.registerTrigger({
  type: 'cron',
  function_id: 'reports.daily',
  config: {
    schedule: '0 9 * * *' // Every day at 9 AM
  }
});

// Every 15 minutes
iii.registerTrigger({
  type: 'cron',
  function_id: 'health.check',
  config: {
    schedule: '*/15 * * * *'
  }
});

Queue Triggers

Subscribe functions to queue topics for async job processing.
iii.registerTrigger({
  type: 'queue',
  function_id: 'orders.process',
  config: {
    topic: 'orders.created'
  }
});

// Multiple subscribers to the same topic
iii.registerTrigger({
  type: 'queue',
  function_id: 'notifications.send',
  config: {
    topic: 'orders.created'
  }
});

Stream Triggers

React to real-time stream events over WebSocket channels.
iii.registerTrigger({
  type: 'stream',
  function_id: 'analytics.track',
  config: {
    channel: 'user-events'
  }
});

Registering Triggers

Basic Registration

import { init } from 'iii-sdk';

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

// 1. Register the function
iii.registerFunction({ id: 'greet' }, async (input) => {
  return { message: `Hello, ${input.name}!` };
});

// 2. Register the trigger
iii.registerTrigger({
  type: 'http',
  function_id: 'greet',
  config: {
    api_path: 'greet',
    http_method: 'POST'
  }
});

// Now accessible at: POST http://localhost:3111/greet

Dynamic Triggers

Triggers can be registered and unregistered at runtime:
// Register a trigger
const triggerId = await iii.registerTrigger({
  id: 'my-http-trigger', // Optional explicit ID
  type: 'http',
  function_id: 'users.list',
  config: { api_path: 'users', http_method: 'GET' }
});

// Later, unregister it
await iii.unregisterTrigger(triggerId);

Custom Trigger Types

You can create custom trigger types by registering them from a worker:
// Register a custom trigger type
iii.registerTriggerType({
  id: 'slack',
  description: 'Trigger functions from Slack events'
});

// Handle trigger registration
iii.on('registerTrigger', async (trigger) => {
  if (trigger.type === 'slack') {
    const { event_type, channel } = trigger.config;
    
    // Set up Slack webhook listener
    slackClient.on(event_type, async (event) => {
      if (event.channel === channel) {
        // Fire the function
        await iii.call(trigger.function_id, event);
      }
    });
  }
});

// Now others can use your trigger type
iii.registerTrigger({
  type: 'slack',
  function_id: 'handle.message',
  config: {
    event_type: 'message',
    channel: '#general'
  }
});

Trigger Lifecycle

The trigger registration flow involves coordination between workers and the engine:

Protocol Messages

Under the hood, triggers use these WebSocket protocol messages:

RegisterTriggerType

Workers declare support for a trigger type:
{
  "type": "registertriggertype",
  "id": "http",
  "description": "HTTP API routes"
}

RegisterTrigger

Create a specific trigger instance:
{
  "type": "registertrigger",
  "id": "trigger-uuid",
  "trigger_type": "http",
  "function_id": "users.create",
  "config": {
    "api_path": "users",
    "http_method": "POST"
  }
}

TriggerRegistrationResult

Engine confirms trigger registration:
{
  "type": "triggerregistrationresult",
  "id": "trigger-uuid",
  "trigger_type": "http",
  "function_id": "users.create",
  "error": null
}

UnregisterTrigger

Remove a trigger:
{
  "type": "unregistertrigger",
  "id": "trigger-uuid",
  "trigger_type": "http"
}

Internal Implementation

From the engine’s Rust implementation:
pub struct Trigger {
    pub id: String,
    pub trigger_type: String,
    pub function_id: String,
    pub config: Value,
    pub worker_id: Option<Uuid>,
}

pub struct TriggerType {
    pub id: String,
    pub _description: String,
    pub registrator: Box<dyn TriggerRegistrator>,
    pub worker_id: Option<Uuid>,
}

pub trait TriggerRegistrator: Send + Sync {
    fn register_trigger(&self, trigger: Trigger) 
        -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send>>;
    fn unregister_trigger(&self, trigger: Trigger) 
        -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send>>;
}
The TriggerRegistry maintains all registered trigger types and trigger instances. When a worker disconnects, all its triggers are automatically unregistered.

Firing Triggers Manually

You can manually fire all triggers of a specific type from the engine:
// From within a module or engine code
engine.fire_triggers("user.created", json!({
    "user_id": "123",
    "email": "[email protected]"
})).await;
This invokes all functions registered to triggers of that type.

Best Practices

Use specific trigger IDs

Provide explicit IDs for triggers you’ll need to unregister later

Handle registration errors

Check TriggerRegistrationResult for errors and retry if needed

Clean up on disconnect

The engine auto-cleans triggers when workers disconnect, but unregister explicitly when possible

Keep configs simple

Trigger configs should be JSON-serializable and well-documented

Next Steps

Functions

Learn about the functions that triggers invoke

Architecture

Understand the engine and worker model

Build docs developers (and LLMs) love