Skip to content

Repository files navigation

Nuxt Queue

npm version npm downloads License Nuxt

Background job queue for Nuxt applications, powered by BullMQ and Redis.

Table of Contents

What You Get

  • File-based jobs (server/jobs/**) with auto-discovery
  • Separate worker process for background execution
  • Redis-backed queues via BullMQ
  • Real-time progress/status updates
  • Job lifecycle hooks (onCompleted, onFailed)
  • Queue stats + cancel/retry endpoints
  • Full TypeScript support

Quick Start

1. Prerequisite

Run Redis:

docker run -d -p 6379:6379 redis

2. Install

npx nuxi@latest module add nuxt-queuekit

Or manually:

npm install nuxt-queuekit
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-queuekit'],
})

3. Start app + worker

# terminal 1
npm run dev

# terminal 2
npx nuxt-queuekit worker

That is enough to run jobs using default Redis (127.0.0.1:6379).

Core Workflow

Step 1: Define a job

Create a file in server/jobs:

// server/jobs/SendWelcomeEmail.ts
interface WelcomeEmailData {
  userId: string
  email: string
  name: string
}

export default defineJob<WelcomeEmailData>({
  async handle(data) {
    await sendEmail({
      to: data.email,
      subject: 'Welcome!',
      template: 'welcome',
      data: { name: data.name },
    })

    return { success: true }
  },
})

defineJob is auto-imported in job files.

Step 2: Dispatch from server runtime

Use dispatch() in server runtime code (API routes, server plugins, server utilities):

// server/api/register.post.ts
export default defineEventHandler(async (event) => {
  const { email, name } = await readBody(event)
  const user = await createUser({ email, name })

  const { jobId } = await dispatch('SendWelcomeEmail', {
    userId: user.id,
    email: user.email,
    name: user.name,
  })

  return { success: true, jobId }
})

Step 3: Dispatch from client components

Use useQueue() on the client:

<script setup lang="ts">
const queue = useQueue('default')

async function start() {
  const { jobId, progress, status, result, error } = await queue.add('SendWelcomeEmail', {
    userId: '1',
    email: 'user@example.com',
    name: 'User',
  })
}
</script>

Organize jobs in folders

Nested files map to dot notation:

server/jobs/
  emails/NewsletterEmail.ts
  media/ProcessImage.ts
await dispatch('emails.NewsletterEmail', { ... })
await dispatch('media.ProcessImage', { ... })

Job options and retries

export default defineJob({
  queue: 'payments',
  options: {
    attempts: 3,
    backoff: { type: 'exponential', delay: 2000 },
  },
  async handle(data) {
    return processPayment(data)
  },
})

Lifecycle hooks

export default defineJob({
  async handle(data, job) {
    await job.updateProgress(50)
    const output = await generateReport(data)
    await job.updateProgress(100)
    return output
  },

  async onCompleted(job, result) {
    await notifyUser(job.data.userId, result)
  },

  async onFailed(job, error) {
    await alertAdmin(job?.data?.userId, error.message)
  },
})

Monitor and Control Jobs

Check one job

const status = await $fetch(`/api/queue/${queueName}/${jobId}`)
// status.state -> waiting | active | completed | failed | delayed

Stream live events (SSE)

const es = new EventSource(`/api/queue/${queueName}/${jobId}/events`)

Cancel waiting/delayed job

await $fetch(`/api/queue/${queueName}/${jobId}/cancel`, { method: 'POST' })

Retry failed job

await $fetch(`/api/queue/${queueName}/${jobId}/retry`, { method: 'POST' })

Queue statistics

const { stats, loading, error, refresh } = useQueueStats('default')
await refresh()

Configuration

Environment variables

NUXT_REDIS_HOST=127.0.0.1
NUXT_REDIS_PORT=6379
NUXT_REDIS_PASSWORD=
NUXT_REDIS_USERNAME=
NUXT_REDIS_DB=0

Module options

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-queuekit'],
  queue: {
    redis: {
      host: process.env.REDIS_HOST || '127.0.0.1',
      port: Number(process.env.REDIS_PORT) || 6379,
      password: process.env.REDIS_PASSWORD,
      username: process.env.REDIS_USERNAME,
      db: Number(process.env.REDIS_DB) || 0,
    },

    // default: server/jobs
    jobsDir: 'server/jobs',

    // optional extra jobs
    jobs: {
      SendNotification: './server/custom-jobs/SendNotification.ts',
    },
  },
})

jobs entries are appended to auto-discovered jobs from jobsDir.

Production

Run web server and workers as separate processes:

npm run build
npm run start
npx nuxt-queuekit worker --concurrency 10

Worker CLI

npx nuxt-queuekit worker [options]

Options:
  --cwd <path>              Working directory (default: current directory)
  --queue <name>            Single queue name (legacy flag)
  --queues <names>          Comma-separated queues in priority order
  --worker <path>           Path to custom worker script
  --concurrency <number>    Concurrent jobs per worker (default: 5)

Examples:

npx nuxt-queuekit worker
npx nuxt-queuekit worker --queue emails --concurrency 10
npx nuxt-queuekit worker --queues critical,default,low --concurrency 5
npx nuxt-queuekit worker --queue processing --worker ./workers/custom.ts

API Reference

defineJob<T, R>(definition)

export default defineJob<{ userId: string }, { ok: true }>({
  queue: 'default',
  options: { attempts: 3 },
  async handle(data, job) {
    await job.updateProgress(100)
    return { ok: true }
  },
  async onCompleted(job, result) {},
  async onFailed(job, error) {},
})

dispatch(jobName, data, options?)

Server runtime helper that creates a job and returns reactive refs:

const { jobId, queueName, progress, status, result, error, refresh, cancel, retry } =
  await dispatch('SendEmail', { to: 'user@example.com' })

useQueue(queueName?)

const queue = useQueue('default')
const job = await queue.add('SendEmail', { to: 'user@example.com' })

useServerQueue(queueName?)

Returns BullMQ Queue directly (advanced/non-reactive):

const queue = useServerQueue('emails')
await queue.add('send-email', { to: 'user@example.com' })

useQueueStats(queueName = 'default')

const { stats, loading, error, refresh } = useQueueStats()
await refresh()

REST endpoints

  • POST /api/queue/add
  • GET /api/queue/:queueName/:jobId
  • GET /api/queue/:queueName/:jobId/events
  • POST /api/queue/:queueName/:jobId/cancel
  • POST /api/queue/:queueName/:jobId/retry
  • GET /api/queue/:queueName/stats

Troubleshooting

Job not registered

  • Ensure file is under configured jobsDir
  • Ensure default export is defineJob({ ... })
  • Restart dev server and worker after adding new files

Worker is running but jobs do not process

  • Confirm Redis is reachable from worker process
  • Confirm queue names match (job.queue vs worker --queue/--queues)

No real-time updates

  • Ensure worker is running (worker publishes events)
  • Check network/proxy supports SSE

License

MIT License

About

Easy to use background job queue for Nuxt applications powered by BullMQ and Redis.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages