Background jobs
Production-grade full-stack SaaS skills for AI coding agents — Next.js, Postgres/Drizzle, Auth, Stripe & Vercel patterns for Codex, Claude Code, Cursor & OpenCode.
npx -y skills add param087/saas-starter-skills --skill background-jobsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when work shouldn't run in the request path — offload emails, webhooks, exports, and scheduled tasks to a queue or job runner with retries, idempotency, and dead-letter handling instead of blocking the user.
SKILL.md
2.6 KB, as published. Nobody here has run it
Background Jobs
Overview
Anything slow, flaky, or scheduled belongs off the request path: sending email, processing uploads, calling third-party APIs, nightly rollups. Use a job system — Inngest or Trigger.dev (serverless-friendly), or BullMQ + Redis (self-hosted) — that gives you retries, backoff, and observability. The request just enqueues and returns; the worker does the work with idempotency so retries are safe.
When to use
- A task takes more than ~1s or calls an external API that can fail.
- Scheduled/recurring work (digests, cleanup, syncing).
- Fan-out work (notify every member of an org).
The pattern (Inngest-style)
// src/server/jobs/send-invite.ts
import { inngest } from "@/server/jobs/client";
import { sendInvite } from "@/server/email/send";
export const sendInviteJob = inngest.createFunction(
{ id: "send-invite", retries: 4 }, // automatic backoff
{ event: "org/member.invited" },
async ({ event, step }) => {
await step.run("send-email", () =>
sendInvite(event.data.email, event.data.org, event.data.url),
);
},
);
// in your action — return fast, do work later
await inngest.send({ name: "org/member.invited", data: { email, org, url } });
Reliability rules
- Idempotent handlers. Key on a stable id so a retry doesn't double-send or double-charge.
- Retries with backoff, then a dead-letter path for poison jobs you can inspect and replay.
- Scope every job to its tenant — pass
orgIdin the payload and re-resolve, never assume. - Schedule with cron triggers for recurring work instead of a hand-rolled timer.
- Keep payloads small — pass ids, fetch fresh data in the worker (avoids stale/oversized events).
Pitfalls
await-ing slow work in the action — blocks the user and couples success to a third party being up.- Non-idempotent workers — retries duplicate emails/charges. Always key the side effect.
- Fire-and-forget with no retries — transient failures silently drop work; use a real queue.
- Putting secrets/large blobs in the event — reference them; fetch in the worker.
- No visibility — without job logs/metrics you can't tell a stuck queue from an empty one.
Hand-off
Durable async work with retries. transactional-email and file-uploads-and-storage run here; observability-and-errors watches failures and dead-letters.