Events & Pub/Sub
Ironflow provides a complete event-driven architecture with three primary capabilities:
- Trigger Events: Start recorded executions (durable workflows) and notify real-time subscribers. Events are permanent recorded facts.
- Topics: Broadcast messages between services without triggering workflows.
- Real-time Subscriptions: Monitor execution and business logic via WebSocket or ConnectRPC.
Architecture
Ironflow separates recorded execution from real-time distribution:
Key behavior: Events are published to the Event Stream immediately. This enables real-time dashboards and monitoring even if no functions are registered to handle the event.
Choosing the Right Method
| Method | Namespace | Triggers Workflows? | Use Case |
|---|---|---|---|
emit() | events: | Yes | Business events like order.placed. |
publish() | topic: | No | Service-to-service messages or notifications. |
invoke() | N/A | Yes | Calling one workflow from another (RPC style). |
SDK Examples & Error Handling
import { createClient, ironflow, IronflowError } from "@ironflow/node";const client = createClient({ apiKey: "ifkey_..." });
// 1. Emit — from anywhere (server, script, handler)try { const { runIds } = await client.emit("order.placed", { id: "123" });
// 2. Publish — from anywhere await client.publish("notifications.email", { to: "user@example.com" });} catch (err) { if (err instanceof IronflowError) { console.error(`Request failed: ${err.message} (Code: ${err.code})`); }}
// 3. Invoke — step primitive, only inside a function handlerexport const orderHandler = ironflow.createFunction( { id: "order-handler", triggers: [{ event: "order.placed" }] }, async ({ step }) => { // result is the typed return value of the target function const result = await step.invoke("generate-pdf", { id: "123" }); return result; },);// 1. Emit — from anywhere (uses context.Context)res, err := client.Emit(ctx, "order.placed", map[string]any{"id": "123"})if err != nil { log.Fatalf("Failed to emit: %v", err)}
// 2. Publish — from anywhere_, err = client.Publish(ctx, "notifications.email", map[string]any{"to": "user@example.com"})if err != nil { log.Fatalf("Failed to publish: %v", err)}
// 3. Invoke — step primitive, only inside a function handler.// `hctx` here is ironflow.Context (handler context), NOT context.Context.var OrderHandler = ironflow.CreateFunction(ironflow.FunctionConfig{ ID: "order-handler", Triggers: []ironflow.Trigger{{Event: "order.placed"}},}, func(hctx ironflow.Context) (any, error) { result, err := ironflow.Invoke[Invoice](hctx, "generate-pdf", map[string]any{"id": "123"}) if err != nil { return nil, err // Let the workflow retry } return result, nil})In This Section
- Event Types - System vs. User events.
- Topics - Pure messaging without workflows.
- Subscriptions - Wildcard patterns and connection lifecycle.
- Consumer Groups - Load-balanced delivery.
- Manual Acknowledgment - Exactly-once patterns.
- CEL Filtering - Server-side content filtering.
- Best Practices - Resource management and error handling.