Prompt of the Day: Create a Background Job with QStash or Inngest
Part 26 of 30 in the Prompt of the Day series.
Serverless functions are brilliant at handling quick, stateless work -- but they come with a hard ceiling. On Vercel, your functions time out at 10 seconds on the Hobby plan and 60 seconds on Pro. That is fine for an API response, but it is a problem the moment you need to send a welcome email, process an uploaded image, or generate a PDF report. Background job systems solve this by offloading the long-running work to a separate invocation triggered asynchronously. The trick is knowing how to wire one up so your AI coding tool does the job right.
The Prompt
I am building a Next.js 14 app deployed on Vercel. I need to set up a background job system for processing user signup events (sending a welcome email via Resend and creating a Stripe customer record).
Vercel serverless functions time out at 60 seconds on Pro, so this work must be offloaded asynchronously.
Please implement this using EITHER:
- QStash from Upstash (simple HTTP-based queue, good for straightforward fire-and-forget jobs), OR
- Inngest (event-driven, better for multi-step workflows with retries and state)
Show me both options side-by-side so I can choose.
For each option, provide:
1. The publisher: a POST /api/signup route handler that enqueues the job immediately after the user record is created in the database
2. The consumer: a POST /api/jobs/welcome route handler (QStash) or an Inngest function (Inngest) that receives the job payload and performs the work
3. Signature/webhook verification so only the queue service can call the consumer
4. Retry logic: at least 3 retries with exponential backoff on failure
5. Error handling with structured logging (console.error with context object)
6. Full TypeScript types for the job payload
Use the @upstash/qstash SDK and the inngest SDK respectively. Include all required environment variable names in comments.
Why It Works
This prompt is effective for three reasons.
Specificity of constraints. Naming the 60-second timeout and the exact deployment target (Vercel) gives the AI the context it needs to make meaningful architectural choices rather than defaulting to a Redis-backed queue that would not survive a serverless cold start.
Concrete use case. "Send a welcome email and create a Stripe customer" is a realistic task with clear I/O. Vague prompts like "set up background jobs" produce skeleton code. A real use case produces code you can actually run.
Structured comparison request. Asking for both QStash and Inngest side-by-side forces the AI to explain the tradeoffs rather than picking one arbitrarily. QStash is the right call for simple fire-and-forget jobs; Inngest shines when you need multi-step workflows, durable state, or branch-per-preview-environment support -- which became even easier in July 2025 when Inngest joined the Vercel Marketplace with one-click install and integrated billing.
The Anti-Prompt
Add a background job to my Next.js app
This fails in every dimension. There is no deployment context, no use case, no preference between the dozen available tools, no mention of timeouts or retries, and no instruction about TypeScript. The AI will either pick a tool at random, generate a generic Node.js worker that cannot run on Vercel, or ask you four clarifying questions before writing a single line. You have handed it a blank canvas with no paint.
Variations
Variation 1 -- QStash with scheduled retry budget
Add this to the base prompt when you need fine-grained control over retry timing:
For the QStash option, configure the queue with:
- retries: 3
- backoff type: exponential
- delay before first retry: 10 seconds
Use the verifySignatureAppRouter helper from @upstash/qstash/nextjs to protect the consumer route.
Variation 2 -- Inngest multi-step workflow
Use this when the job has sequential steps that each need their own retry boundary:
For the Inngest option, model the job as a multi-step function using step.run().
Step 1: create the Stripe customer and store the customer ID to the database.
Step 2: send the welcome email via Resend using the stored customer ID.
Each step should retry independently so a Resend outage does not re-run the Stripe call.
Variation 3 -- Trigger.dev for jobs without timeouts
When 60 seconds is genuinely not enough (PDF generation, video transcoding, bulk data exports):
Instead of QStash or Inngest, use Trigger.dev v3.
Define a task using the task() API from @trigger.dev/sdk.
The task has no timeout limit and runs in Trigger.dev's managed runtime, not in a Vercel function.
Trigger it from my Next.js route handler using myTask.trigger(payload).
Trigger.dev v3 reached general availability in September 2024 with a fully managed, no-timeout runtime -- a direct answer to the serverless ceiling problem.
When You Would Actually Use This
The pattern in this prompt fits any flow where a user action kicks off work that should not block the HTTP response: post-signup onboarding sequences, image thumbnail generation after upload, PDF report generation triggered by a dashboard button, or nightly data sync jobs. Upstash's QStash documentation shows the pattern clearly: publish the job with publishJSON, receive it at a verified webhook endpoint, and let QStash handle retries if your consumer throws. The user gets a fast 200 response; the queue handles the rest.
Ask The Guild
Which tool are you using for background jobs in your current project -- QStash, Inngest, Trigger.dev, or something else entirely? Drop your stack and the use case in the thread. Bonus points if you have hit the Vercel timeout wall and have a war story to share.