Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

Tasks

The typed, durable unit of job execution.

A task defines the input, result, and behavior of work that a job can run durably.

Scaffold a task

From the application root, with an existing orders service:

bun run relkit add task export-orders --service orders

This creates src/services/orders/tasks/export-orders.task.ts with the stable ID orders.export-orders-task, version 1, and a durable execution model. It registers the task on the service. Replace the placeholder schemas and handler with your operation; keep its identity and version stable. Expose it through a task-first job when callers need admission, run handles, and observation.

For every task scaffolding flag, see the task command reference.

Define the task

A task contains the code and schemas for one durable operation. A task is not a request handler: it may be resumed, retried, observed, or run after the original process exits.

/** Produces demonstration CSV through the ordinary order helper and emits validated progress around the durable export. */
import { defineTask } from "@relkit/app/tasks";
import { z } from "@relkit/app/schema";
import { exportOrderCsv } from "../helpers/fake-business.js";

const exportOrders = defineTask({
  id: "orders.export-orders",
  version: "1",
  execution: "durable",
  input: z.object({ orderIds: z.array(z.string()) }),
  output: z.object({ csv: z.string(), exported: z.number().int().nonnegative() }),
  progress: z.object({
    completed: z.number().int().nonnegative(),
    total: z.number().int().positive(),
  }),
  handler: async ({ orderIds }, context) => {
    await context.progress.emit({ completed: 0, total: orderIds.length });
    const csv = exportOrderCsv(orderIds);
    await context.progress.emit({ completed: orderIds.length, total: orderIds.length });
    return { csv, exported: orderIds.length };
  },
});

export default exportOrders;

Use a stable task ID. The version identifies a compatible task definition, not a deployment build. Schema hashes and the client fingerprint are recorded with each job definition so migration checks can reject accidental contract changes.

Tasks can emit typed progress, return a bounded output, sleep when their provider supports durable sleep, and throw declared errors for retry or terminal failure. Keep business side effects idempotent and put provider-specific code behind the integration boundary. A retryable task does not expose durable sleep.

See durations and sleep, execution policy, and retries and idempotency.

Use from the frontend

A task does not become a browser endpoint by itself. Give it a job with a client policy, then use the typed job client to trigger and observe it. The task’s schemas determine input and output; the job’s policy determines permitted operations and visible fields.

Reference and source