Skip to main content
A single function can be bound to as many triggers as needed. Just call registerTrigger / register_trigger multiple times with the same function_id. Inside the handler, inspect the input shape to dispatch to the right branch.

Register one function, three triggers

import { registerWorker, Logger, TriggerAction } from 'iii-sdk'

const iii = registerWorker(process.env.III_URL ?? 'ws://localhost:49134')

iii.registerFunction(
  { id: 'orders.handle', description: 'Handles orders from API, queue, or cron' },
  async (input) => {
    const logger = new Logger()

    // HTTP trigger — input is an ApiRequest shape
    if (input && typeof input === 'object' && 'path_params' in input) {
      const req = input as ApiRequest<{ amount: number; description: string }>
      const orderId = `order-${Date.now()}`

      logger.info('Processing manual order via API', { amount: req.body?.amount })

      await iii.trigger({
        function_id: 'state::set',
        payload: {
          scope: 'orders',
          key: orderId,
          value: { id: orderId, ...req.body, source: 'api', createdAt: new Date().toISOString() },
        },
        action: TriggerAction.Void(),
      })

      await iii.trigger({
        function_id: 'enqueue',
        payload: { topic: 'order.processed', data: { orderId, source: 'api' } },
        action: TriggerAction.Void(),
      })

      return { status_code: 200, body: { message: 'Order processed', orderId } }
    }

    // Queue trigger — input is the event payload
    if (input && typeof input === 'object' && 'amount' in input) {
      const { amount, description } = input as { amount: number; description: string }
      const orderId = `order-${Date.now()}`

      logger.info('Processing order from queue', { amount })

      await iii.trigger({
        function_id: 'state::set',
        payload: {
          scope: 'orders',
          key: orderId,
          value: { id: orderId, amount, description, source: 'queue', createdAt: new Date().toISOString() },
        },
        action: TriggerAction.Void(),
      })

      await iii.trigger({
        function_id: 'enqueue',
        payload: { topic: 'order.processed', data: { orderId, amount, source: 'queue' } },
        action: TriggerAction.Void(),
      })

      return
    }

    // Cron trigger — input is null/empty
    logger.info('Processing scheduled order batch')

    const pendingOrders = await iii.trigger<{ id: string; amount: number }[]>({
      function_id: 'state::list',
      payload: { scope: 'pending-orders' },
    })

    for (const order of pendingOrders ?? []) {
      await iii.trigger({
        function_id: 'enqueue',
        payload: { topic: 'order.processed', data: { orderId: order.id, amount: order.amount, source: 'cron' } },
        action: TriggerAction.Void(),
      })
    }

    logger.info('Batch complete', { count: pendingOrders?.length ?? 0 })
  },
)

// Bind all three trigger types to the same function ID
iii.registerTrigger({
  type: 'http',
  function_id: 'orders.handle',
  config: { api_path: 'orders/manual', http_method: 'POST' },
})

iii.registerTrigger({
  type: 'queue',
  function_id: 'orders.handle',
  config: { topic: 'order.created' },
})

iii.registerTrigger({
  type: 'cron',
  function_id: 'orders.handle',
  config: { expression: '* * * * *' },
})

Dual-trigger shorthand

When you only need two trigger types, the pattern is identical — just skip the third registerTrigger call.
iii.registerTrigger({ type: 'queue', function_id: 'my.fn', config: { topic: 'events' } })
iii.registerTrigger({ type: 'http', function_id: 'my.fn', config: { api_path: 'events', http_method: 'POST' } })

Key concepts

  • There is no framework-level ctx.match() — distinguish trigger types by inspecting the input shape at runtime.
  • HTTP trigger input always has path_params, query_params, body, and headers fields.
  • Queue trigger input is the raw event payload you published with enqueue.
  • Cron trigger input is null / None / json!(null) — the invocation itself is the signal.
  • Registering the same function_id with multiple triggers is intentional and supported; the engine dispatches independently.