Subscriptions

Internal pub/sub reactions with optional idempotency.

Subscriptions allow you to react to internal events published via ctx.pubsub.publish(...). They are the choreography layer for decoupling business flows.

Defining a Subscription

import { subscription } from "@chimpbase/runtime";

const onTodoCreated = subscription(
  "todo.created",
  async (ctx, event) => {
    await ctx.query(
      "insert into audit_log (event, payload) values (?1, ?2)",
      ["todo.created", JSON.stringify(event)],
    );
  },
  { idempotent: true, name: "auditTodoCreated" },
);

Publishing Events

Events are published from actions or other handlers using the context:

ctx.pubsub.publish("todo.created", {
  todoId: todo.id,
  title: input.title,
});

Idempotency

Mark subscriptions as idempotent: true with a stable name to deduplicate cross-process delivery. This is important when multiple runtime instances are running:

subscription("order.placed", handleOrder, {
  idempotent: true,
  name: "processNewOrder",
});

The runtime uses _chimpbase_sub.seen:* markers to track which events have been processed.

Multiple Subscribers

Multiple subscriptions can listen to the same event:

const registrations = [
  subscription("todo.completed", auditTodoCompleted, {
    idempotent: true,
    name: "auditTodoCompleted",
  }),
  subscription("todo.completed", enqueueTodoNotification, {
    idempotent: true,
    name: "enqueueTodoNotification",
  }),
];

Using Decorators

import { Subscription } from "@chimpbase/runtime";

class TodoModule {
  @Subscription("todo.created", { idempotent: true })
  async auditTodoCreated(ctx, event) {
    // ...
  }
}