DEV Community

Atlas Whoff
Atlas Whoff

Posted on

Background Jobs in Next.js: Cron, Fire-and-Forget, Trigger.dev, and QStash

The Background Job Problem

Some operations shouldn't happen during a request:

  • Sending welcome emails (users shouldn't wait for SMTP)
  • Resizing images (CPU-intensive, blocks the response)
  • Syncing data to external services
  • Generating reports

Background jobs solve this. Here's how to implement them in Next.js.

Option 1: Vercel Edge Functions + Cron

For scheduled tasks:

// app/api/cron/daily-report/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(req: NextRequest) {
  // Verify the request comes from Vercel Cron
  const authHeader = req.headers.get('authorization')
  if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  await generateDailyReport()

  return NextResponse.json({ success: true })
}
Enter fullscreen mode Exit fullscreen mode
// vercel.json
{
  "crons": [
    {
      "path": "/api/cron/daily-report",
      "schedule": "0 9 * * *"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Option 2: Fire-and-Forget (Simple Background Task)

For operations after responding to the user:

// app/api/users/route.ts
export async function POST(req: NextRequest) {
  const { email, name } = await req.json()

  // Create user synchronously
  const user = await db.user.create({ data: { email, name } })

  // Send email asynchronously -- don't await
  // Vercel will keep the function alive for background work
  sendWelcomeEmail(user.email, user.name).catch(err => {
    logger.error({ err, userId: user.id }, 'Failed to send welcome email')
  })

  // Respond immediately
  return NextResponse.json({ user })
}
Enter fullscreen mode Exit fullscreen mode

Option 3: Trigger.dev (Full Job Queue)

For complex, retriable background jobs:

npm install @trigger.dev/sdk @trigger.dev/nextjs
Enter fullscreen mode Exit fullscreen mode
// jobs/send-welcome-email.ts
import { client } from '@/trigger'
import { eventTrigger } from '@trigger.dev/sdk'
import { z } from 'zod'

client.defineJob({
  id: 'send-welcome-email',
  name: 'Send Welcome Email',
  version: '0.0.1',
  trigger: eventTrigger({
    name: 'user.created',
    schema: z.object({ userId: z.string(), email: z.string() })
  }),
  run: async (payload, io) => {
    await io.runTask('send-email', async () => {
      await sendWelcomeEmail(payload.email)
    })

    await io.runTask('provision-resources', async () => {
      await createDefaultWorkspace(payload.userId)
    })
  }
})

// Trigger from API route
await client.sendEvent({
  name: 'user.created',
  payload: { userId: user.id, email: user.email }
})
Enter fullscreen mode Exit fullscreen mode

Option 4: QStash (Vercel-Native Job Queue)

import { Client } from '@upstash/qstash'

const qstash = new Client({ token: process.env.QSTASH_TOKEN! })

// Queue a job from any API route
await qstash.publishJSON({
  url: `${process.env.NEXTAUTH_URL}/api/jobs/process-payment`,
  body: { userId, amount, currency },
  delay: 0,
  retries: 3,
})

// The job handler
// app/api/jobs/process-payment/route.ts
import { verifySignatureAppRouter } from '@upstash/qstash/nextjs'

export const POST = verifySignatureAppRouter(async (req) => {
  const { userId, amount } = await req.json()
  await processPayment(userId, amount)
  return NextResponse.json({ success: true })
})
Enter fullscreen mode Exit fullscreen mode

When to Use Each

Vercel Cron:
  Best for: scheduled tasks (daily reports, cleanup jobs)
  Limit: 1 per minute minimum interval (free), every second (pro)

Fire-and-forget:
  Best for: simple notifications, logging, analytics
  Risk: if function times out, job is lost

Trigger.dev:
  Best for: complex workflows, retries, observability
  Best for: when you need to see job history and debug failures

QStash:
  Best for: Vercel deployments, simple delayed jobs, webhooks
  Best for: when you're already using Upstash Redis
Enter fullscreen mode Exit fullscreen mode

Ship Background Jobs Pre-Configured

The AI SaaS Starter Kit includes fire-and-forget email sending and Vercel cron setup for scheduled tasks.

$99 one-time at whoffagents.com

Top comments (0)