Skip to content

Jobs

SemitraJob is the background-work subsystem in Semitra.

Use it for durable work that should not block the request path.

Jobs package a unit of work with:

  • a job name
  • a queue name
  • a validated payload
  • a trace ID
  • attempt metadata
  • retry metadata
  • tenant context
  • optional scheduling metadata

That makes job execution observable and retryable instead of ad hoc.

The main entry points on SemitraJob are:

  • schedule(payload, options) to build a dispatch envelope without sending it
  • dispatch(payload, options) to enqueue a job to the provided target
  • dispatchTo(queue, payload, options) to send directly to a queue target
  • performEnvelope(envelope, options) to execute a queued job envelope
  • payload schemas to validate the payload before work starts

The example app uses a mail job to deliver a welcome email:

import { s, type SemitraMailRuntime } from "@semitra/cli";
import WelcomeMailer from "../mailers/welcome_mailer.ts";
import ApplicationJob from "./application_job.ts";
export const WelcomeEmailJobPayload = s.object({
email: s.string(),
title: s.string().min(3)
});
export default class WelcomeEmailJob extends ApplicationJob<
{ email: string; title: string },
SemitraMailRuntime
> {
static payload = WelcomeEmailJobPayload;
static retries = 1;
async perform(payload, context): Promise<void> {
const runtime = context.runtime;
if (!runtime) {
throw new Error("WelcomeEmailJob requires a mail runtime");
}
await WelcomeMailer.deliver(payload, runtime);
}
}

If a job fails, Semitra can reschedule it using the class-level retries setting and optional retryDelay.

That gives you common background-work behavior without wiring custom retry logic into every queue consumer.

  • send a welcome email after signup
  • recalculate denormalized counters after a write
  • sync data to an external API after a transaction completes
  • generate a report or export after the request returns
  • defer expensive side effects that do not belong in the controller path

Do not use a job when the work is:

  • required for the immediate HTTP response
  • cheap enough to do inline
  • better modeled as an event listener

Jobs are for durable execution, not for hiding ordinary request work.