Background Jobs / Deferred Work
Run work after the response without blocking the request — and know when you actually need a real queue.
NextRush does not ship a job-queue package — there is no @nextrush/queue among its 35
packages (Packages directory). For a single-process app, setImmediate/an
async fire-and-forget call after ctx.json() covers "send a welcome email, don't make the
client wait for it." For anything that must survive a crash or run across multiple instances,
reach for a real queue (BullMQ + Redis, or a managed service) — this recipe shows the boundary,
not a replacement for one.
Solution
type Job = () => Promise<void>;
/**
* Minimal in-process, in-memory job queue. Runs one job at a time, in order.
* Jobs are lost on crash/restart — this is a request-decoupling tool, not a
* durability guarantee. Reach for BullMQ/Redis (or a managed queue) the
* moment "survives a restart" or "runs across multiple instances" matters.
*/
class InProcessQueue {
private queue: Job[] = [];
private running = false;
enqueue(job: Job): void {
this.queue.push(job);
void this.drain();
}
private async drain(): Promise<void> {
if (this.running) return;
this.running = true;
while (this.queue.length > 0) {
const job = this.queue.shift()!;
try {
await job();
} catch (err) {
console.error('[jobs] job failed', err);
}
}
this.running = false;
}
}
export const jobs = new InProcessQueue();import { createApp, listen } from 'nextrush';
import { jobs } from './jobs/in-process-queue';
import { sendWelcomeEmail } from './email';
const app = createApp();
app.post('/signup', (ctx) => {
const { email } = ctx.body as { email: string };
// Respond immediately; the email send happens after this handler returns.
jobs.enqueue(() => sendWelcomeEmail(email));
ctx.status = 202;
ctx.json({ status: 'accepted' });
});
listen(app, 8080);ctx.next() is irrelevant here — this is a route handler, not middleware — the point is that
jobs.enqueue() returns synchronously and the response is sent before sendWelcomeEmail
resolves. 202 Accepted (rather than 200/201) signals to the caller that the work is
deferred, not necessarily done by the time the response arrives. See
Context for ctx.status/ctx.json.
Not durable
InProcessQueue holds jobs in memory. A crash, redeploy, or pm2 restart between enqueue()
and the job running loses that job silently. If losing a queued email is unacceptable, this
pattern is the wrong tool — use a persisted queue instead.
Try It
curl -i -X POST http://localhost:8080/signup \
-H "Content-Type: application/json" \
-d '{"email":"new-user@example.com"}'Expected result: 202 Accepted with {"status":"accepted"} returned immediately, followed
shortly after (in the server logs) by the welcome email actually sending — the client never
waits for it.
Related
- Packages directory — confirms no queue/job package ships with NextRush
- Context —
ctx.status,ctx.json - Reliability — graceful shutdown considerations if you adopt this pattern (in-flight jobs on
SIGTERM)