Next.js background jobs: when after(), Server Actions, and Route Handlers stop being enough
Next.js App Router gives you <code>after()</code>, Server Actions, and Route Handlers for post-response work. Each is the right tool for a specific job—and each has a failure mode that pushes you toward an external worker tier. This page is a decision guide: read the table, pick the right primitive, and see how to hand off to an Inquir pipeline when you need to outlast the HTTP window.
Last updated: 2026-06-28
- Use after(): quick post-response side effects that complete inside the platform timeout.
- Use Server Actions: form-driven mutations; hand off to Inquir when the side effect is slow or must retry independently.
- Use Route Handler + Inquir: explicit 202 ack, job queued on Inquir, user never waits on slow work.
- Use full Inquir pipeline: multi-step orchestration, cron scheduling, parallel fan-out, webhook continuation.
Answer first
Direct answer
Next.js background jobs: when after(), Server Actions, and Route Handlers stop being enough. Your Server Action or Route Handler calls an Inquir gateway endpoint with an API key and returns 202 to the client immediately. Inquir pipelines handle queuing, retries, secrets, and execution history—the same functions you use for cron and webhooks.
When it fits
- The side effect in your Server Action or Route Handler can fail independently, needs retries, or must outlast the platform timeout.
- You want cron-scheduled jobs, webhook continuation, or parallel fan-out with a visible run history beside your Next.js App Router code.
Tradeoffs
after()and Fluid Compute are the right choice for IO-bound work that completes inside the platform timeout—logging, analytics events, cache warming, a single downstream API call.- Vercel Cron in
vercel.jsonhandles lightweight scheduled tasks that finish in seconds. The moment you need multi-step orchestration, per-step retries, visible run history, or work that genuinely outlasts the function invocation, you need a dedicated job tier.
Workload and what breaks
Why Next.js background primitives hit a wall
after() and Server Actions defer work until after the response—but on Vercel they still run inside the same function invocation with platform timeouts. PDF generation, bulk email, slow third-party API chains, and large file transforms all outlast those limits. The primitives are not broken; the work simply does not belong inside a request handler.
Teams then reach for BullMQ, Inngest, or a separate worker container. That means a second deploy target, a second secret store, and a second dashboard when a Stripe webhook or nightly sync fails at 3 AM.
Trade-offs
When Next.js built-in tooling is enough
after() and Fluid Compute are the right choice for IO-bound work that completes inside the platform timeout—logging, analytics events, cache warming, a single downstream API call.
Vercel Cron in vercel.json handles lightweight scheduled tasks that finish in seconds. The moment you need multi-step orchestration, per-step retries, visible run history, or work that genuinely outlasts the function invocation, you need a dedicated job tier.
How Inquir helps
Next.js stays thin; Inquir runs the slow path
Your Server Action or Route Handler calls an Inquir gateway endpoint with an API key and returns 202 to the client immediately. Inquir pipelines handle queuing, retries, secrets, and execution history—the same functions you use for cron and webhooks.
One workspace for HTTP ingress, scheduled pipelines, and async jobs. No Redis to provision, no worker Dockerfile beside your Next.js repo—just a gateway URL your app already knows how to fetch.
Compare
Next.js background primitive decision table
Pick the row that matches your work. Each primitive has a failure mode—knowing it helps you decide when to hand off to an Inquir pipeline.
| Primitive | Best for | Failure mode | Use Inquir when |
|---|---|---|---|
| after() | Quick post-response side effects (analytics, cache bust, single API call) | Still runs inside the function invocation; times out with the request on Vercel | Work can fail independently, needs retries, or outlasts the platform timeout |
| Server Action | Form-driven mutations: create, update, validate—with optimistic UI | Long side effects block the action; timeout ends the action and silently drops the work | The side effect (email, PDF, sync) is slow or must be retried independently of the form |
| Route Handler (sync) | REST API endpoints: auth, CRUD, short webhook acks | Cannot outlast the function timeout; slow work blocks the response to the client | The caller expects 202 and the work runs asynchronously with retries and history |
| Route Handler + Inquir gateway | Async handoff: validate input, enqueue on Inquir, return 202 immediately | Adds a network hop; Inquir gateway is an external dependency | Work is slow, needs per-step retries, or must run on a cron schedule separately |
| Full Inquir pipeline | Multi-step orchestration, cron scheduling, webhook continuation, parallel fan-out | Requires external service; more ops than after() for trivial side effects | You need this when the work has multiple steps, must retry at each step, or runs on a schedule |
What you get
Next.js App Router + Inquir: integration patterns
Server Action handoff
Server Action validates the form and enqueues on Inquir; the action returns immediately. The pipeline step runs the slow side effect—email send, PDF build, data sync—outside the action timeout.
Route Handler 202 ack
App Router POST handler validates input, calls the Inquir gateway, and returns 202 before the pipeline starts. The client polls or the pipeline calls back when done.
Webhook continuation
An Inquir gateway endpoint acks Stripe or GitHub within the provider timeout window, then continues in a pipeline step. Provider retries do not re-run expensive work.
Cron beyond vercel.json
Nightly ETL, token rotation, and report generation run as Inquir cron-triggered pipelines with visible run history and per-step retries—not a fragile Route Handler behind a Vercel cron hit.
What to do next
How to wire Next.js to Inquir pipelines
Server Actions and Route Handlers stay thin: validate, enqueue on Inquir, return to the client. Pipelines carry retries, secrets, and execution history for the slow path.
Extract the slow logic to an Inquir function
Move email, PDF, or sync work into an Inquir handler or pipeline step. Keep inputs explicit; make handlers idempotent so retries are safe.
Enqueue from a Server Action or Route Handler
fetch the Inquir gateway URL with Authorization: Bearer from process.env.INQUIR_API_KEY. Return 202 or redirect before the pipeline finishes.
Monitor in Inquir execution history
Every run, retry, and failure appears in Inquir—no digging through Vercel function logs for work that started in a Server Action or after().
Code example
Next.js Server Action → Inquir: enqueue after form submit
The Server Action validates the form and hands off to an Inquir gateway endpoint. The endpoint enqueues an async job and returns 202. The pipeline step builds the asset and notifies the app. The user sees success immediately; the slow work runs outside any Next.js or Vercel function timeout.
'use server'; export async function requestExport(formData: FormData) { const exportId = formData.get('exportId') as string; if (!exportId) throw new Error('exportId required'); // Hand off to Inquir — returns before the pipeline finishes const res = await fetch(process.env.INQUIR_GATEWAY_URL + '/jobs/run-export', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.INQUIR_API_KEY}`, }, body: JSON.stringify({ exportId }), }); if (!res.ok) throw new Error('Failed to queue export'); // Server Action returns; user sees success — Inquir runs the slow work }
export async function handler(event) { const { exportId } = JSON.parse(event.body || '{}'); if (!exportId) { return { statusCode: 400, body: JSON.stringify({ error: 'exportId required' }) }; } // Enqueue async work; the pipeline step runs outside the HTTP window const { instanceId: jobId } = await global.durable.startNew('run-export', undefined, { exportId }); return { statusCode: 202, body: JSON.stringify({ jobId, status: 'queued' }) }; }
export async function handler(event) { // event.payload carries the body the Server Action sent const { exportId } = event.payload ?? {}; const file = await buildExport(exportId); await storage.put(`exports/${exportId}.csv`, file); await notifyApp(exportId, file.byteLength); return { exportId, bytes: file.byteLength }; }
When it fits
When to reach for an Inquir pipeline
When this works
- The side effect in your Server Action or Route Handler can fail independently, needs retries, or must outlast the platform timeout.
- You want cron-scheduled jobs, webhook continuation, or parallel fan-out with a visible run history beside your Next.js App Router code.
When to skip it
- All background work fits inside
after()or Fluid Compute and you do not need cross-request job history or per-step retries.
FAQ
FAQ
Should I replace after() with Inquir?
after() with Inquir?Not always. Use after() for quick post-response work that completes inside the platform timeout. Use Inquir when the job can fail independently, needs retries, runs on a schedule, or needs to outlast the function invocation.
Can I keep deploying Next.js on Vercel?
Yes. This page is about Next.js background primitives, not hosting. Keep Vercel for the App Router and edge layer; use Inquir as the worker tier.
How is this different from the Vercel background jobs page?
The Vercel page covers framework-agnostic deployment topology—which work lives on Vercel vs Inquir for any framework. This page focuses on Next.js-specific APIs (after(), Server Actions, Route Handlers) and the failure mode of each primitive.
Do I need a separate queue like BullMQ?
No. Inquir pipelines provide queuing, retries, and observability. Your Next.js code calls a gateway endpoint; Inquir manages the rest.