Skip to content

Actions

Actions are the core unit of business logic in Chimpbase. They can be called from HTTP routes, other actions, subscriptions, workers, workflows, and the CLI.

Defining Actions

Simple (tuple args)

ts
import { action } from "@chimpbase/runtime";

const greetUser = action("greetUser", async (ctx, name: string) => {
  ctx.log.info("greeting user", { name });
  return { message: `Hello, ${name}!` };
});

With validation (object args)

ts
import { action, v } from "@chimpbase/runtime";

const createTodo = action({
  name: "createTodo",
  args: v.object({
    title: v.string(),
    projectSlug: v.string(),
    priority: v.optional(v.string()),
    assigneeEmail: v.optional(v.union(v.string(), v.null())),
  }),
  async handler(ctx, input) {
    const [todo] = await ctx.db.query(
      "INSERT INTO todos (title, project_slug, priority, assignee_email) VALUES (?1, ?2, ?3, ?4) RETURNING *",
      [input.title, input.projectSlug, input.priority ?? "medium", input.assigneeEmail ?? null],
    );

    ctx.pubsub.publish("todo.created", todo);
    return todo;
  },
});

Handler Signature

Every action handler receives a ChimpbaseContext as its first argument:

ts
(ctx: ChimpbaseContext, ...args) => TResult | Promise<TResult>

The context provides access to all Chimpbase primitives:

PropertyTypeDescription
ctx.dbChimpbaseDbClientRaw SQL + Kysely queries
ctx.collectionChimpbaseCollectionClientDocument CRUD
ctx.kvChimpbaseKvClientKey-value store
ctx.streamChimpbaseStreamClientAppend-only event streams
ctx.queueChimpbaseQueueClientEnqueue worker jobs
ctx.workflowChimpbaseWorkflowClientStart/signal workflows
ctx.pubsubChimpbasePubSubClientPublish events
ctx.logChimpbaseLoggerStructured logging
ctx.metric()methodRecord metrics
ctx.trace()methodDistributed tracing
ctx.secret()methodRead secrets
ctx.action()methodCall other actions

Calling Actions

From another action

ts
const dashboard = action("getDashboard", async (ctx) => {
  const todos = await ctx.action("listTodos", { status: "backlog" });
  return { backlog: todos.length };
});

From an HTTP route

ts
app.post("/todos", async (c) => {
  const body = await c.req.json();
  const todo = await c.env.action(createTodo, body);
  return c.json(todo, 201);
});

Using action references

When you store an action in a variable, you can pass the reference directly instead of using a string name:

ts
const result = await ctx.action(createTodo, { title: "Ship it", projectSlug: "core" });

Telemetry

Control per-action telemetry persistence:

ts
const noisyAction = action("pollStatus", async (ctx) => {
  // ...
}, { telemetry: false }); // suppress all telemetry

const importantAction = action("chargeCustomer", async (ctx) => {
  // ...
}, { telemetry: { log: true, metric: true, trace: true } });

Validators

The v namespace provides runtime input validation:

ts
import { v } from "@chimpbase/runtime";

v.string()                          // string
v.number()                          // number
v.boolean()                         // boolean
v.null()                            // null
v.unknown()                         // unknown
v.optional(v.string())              // string | undefined
v.nullable(v.string())              // string | null
v.array(v.string())                 // string[]
v.union(v.string(), v.null())       // string | null
v.enum(["low", "medium", "high"])   // "low" | "medium" | "high"
v.literal("active")                 // "active"
v.object({ name: v.string() })      // { name: string }

Invalid input throws a validation error before the handler runs.

Registration

Actions are registered in the registrations array of your app definition:

ts
export default {
  project: { name: "my-app" },
  registrations: [
    createTodo,
    listTodos,
    getDashboard,
  ],
} satisfies ChimpbaseAppDefinitionInput;