omg.devdocs
SDK

Schedules and workflows

Run code on a cron schedule, react to events, and write durable multi-step workflows that survive restarts and sleep. No queue, worker or scheduler to set up.

Every omg.dev app with server code can run background jobs. Put them in a file in functions/, deploy, and omg.dev registers them. There is no queue, worker or scheduler to run. An app that is asleep wakes up when a job is due.

Cron schedules

cron(schedule, handler) runs a function on a 5-field cron expression, in UTC.

functions/jobs.ts
import { cron, db } from "@omg-dev/server"

// Every day at 09:00 UTC
export const digest = cron("0 9 * * *", async () => {
  const tasks = await db.getAll("tasks")
  await db.insert("digests", { count: tasks.length, at: new Date().toISOString() })
})

The schedule must be a string literal. The build reads it to register the job.

Durable workflows

workflow(name, fn) declares a function made of steps. Each step.run() is recorded. If the app restarts or goes to sleep, the workflow continues from the last finished step and does not run a finished step again. step.sleep() can wait for seconds or for days.

functions/onboarding.ts
import { workflow, startWorkflow, email } from "@omg-dev/server"

export const onboarding = workflow("onboarding", async (step, user: { email: string }) => {
  await step.run("welcome", () =>
    email.send({ to: user.email, subject: "Welcome", html: "<p>Glad you are here.</p>" }),
  )
  await step.sleep("one-day", 24 * 60 * 60 * 1000)
  await step.run("tips", () =>
    email.send({ to: user.email, subject: "Three tips", html: "<p>…</p>" }),
  )
})

// Start a run from any route, for example after sign-up:
export async function POST(req: Request) {
  const user = await req.json()
  const { runId } = await startWorkflow("onboarding", user, { id: `onboarding-${user.email}` })
  return Response.json({ runId })
}

One rule: put every side effect (a database write, an email, an API call) inside step.run(). Code between steps can run again when the workflow resumes.

Pass id to startWorkflow to start a run only once for the same key.

Events

on(topic, handler) reacts to an event. emit(topic, payload) sends one. schedule(atMs, topic, payload) sends one at a later time.

functions/events.ts
import { on, emit, schedule } from "@omg-dev/server"

export const welcome = on("user.signup", async (_ctx, payload: { email: string }) => {
  // …
})

// Somewhere in a route:
await emit("user.signup", { email: "ada@example.com" })
await schedule(Date.now() + 60 * 60 * 1000, "reminder.due", { taskId: "t1" })

Local development

With bun run dev, cron jobs, events and workflows run in the dev server process. Local workflow runs are not durable: a restart loses them. Deployed apps keep every step.