· nodejs / typescript / job-queue

Job queue tốt nhất cho Node.js và TypeScript năm 2026

BullMQ chiếm ưu thế cho backend chạy Redis. Trigger.dev và Inngest là giải pháp managed. QStash là lựa chọn duy nhất cho edge. So sánh đầy đủ.

Bởi

3.430 từ · 18 phút đọc

Đang chọn job queue cho Node.js hay TypeScript? Nếu backend của bạn đã chạy Redis, hãy dùng BullMQ v5. Bạn có được 5.6M lượt tải mỗi tuần từ cộng đồng và ecosystem, không có chi phí mỗi lần thực thi, và bộ tính năng đầy đủ — cron, retry, priority, concurrency — mà không cần subscription SaaS.

Nếu bạn muốn không phải quản lý infrastructure, dùng Trigger.dev v4 ($50/tháng base). Nó có giá tốt hơn Inngest ở quy mô lớn và tương đương về TypeScript DX. Nếu bạn đang xây dựng trên Cloudflare Workers, QStash là lựa chọn duy nhất trong danh sách này.

Bài viết này dành cho ai

Các developer Node.js / TypeScript đã vượt quá giới hạn của setTimeout và cron script, và đang chọn một job queue production-grade trong năm 2026. Nếu bạn chưa từng gặp trường hợp job thất bại âm thầm mà không để lại dấu vết, bài viết này sẽ thuyết phục bạn rằng bạn cần một hệ thống như vậy. Nếu bạn đã biết mình cần, hãy bỏ qua đến bảng so sánh.

Những gì chúng tôi đánh giá

Sáu thư viện: BullMQ v5.77.6, Trigger.dev v4.4.6, Inngest SDK v4 (GA ngày 3 tháng 3 năm 2026), QStash (@upstash/qstash), pg-boss v12, và Quirrel.

Số liệu lượt tải lấy từ npm downloads API cho tuần 22–28 tháng 5 năm 2026. Giá được xác minh từ trang pricing của mỗi nhà cung cấp vào ngày 2026-05-30: Trigger.dev, Inngest, Upstash/QStash. Các đoạn code lấy từ tài liệu chính thức của phiên bản stable hiện tại của mỗi thư viện.

Tại sao cần job queue

Các hàm async thông thường và cron script thất bại theo cùng một cách. Khi process khởi động lại, toàn bộ công việc đang chạy sẽ mất. Một API bên ngoài chậm sẽ làm nghẽn request cycle. Khi lượng email tăng đột biến, main thread của bạn sẽ bị kéo xuống. Một cron chạy cùng lúc trên mười server sẽ gửi cho user mười hoá đơn giống hệt nhau.

Một job queue tách riêng công việc khỏi sự kiện kích hoạt nó. Job có tính bền vững: chúng tồn tại qua các lần restart. Chúng được retry khi thất bại với backoff. Chúng chạy trong nền, ngoài request path. Chúng có thể được lên lịch, delay, ưu tiên, và giới hạn tốc độ mà không cần bạn tự xây dựng cơ chế.

Cơ chế cụ thể bạn cần sẽ quyết định thư viện nào phù hợp.

So sánh {#comparison}

Tiêu chíBullMQ v5Trigger.dev v4InngestQStashpg-boss v12Quirrel
Lượt tải/tuần5.6M568K1.1M390K640K1.6K
BackendRedisManaged / self-hostedManaged / self-hostedHTTP (managed)PostgreSQLManaged
TypeScriptNative (56% codebase)NativeNative (SDK v4)NativeCó typesNative
Lên lịch cronupsertJobScheduler✅ (nhiều scheduler/queue)
Job delay✅ (waitpoints)step.sleep()✅ (không giới hạn thời gian)
Retry✅ fixed / exponential / custom✅ per-step✅ miễn phíretryDelayMax
Priority
Concurrency✅ per-worker✅ per-plan✅ per-plan❌ (HTTP push)
DashboardBull Board (OSS)Tích hợp sẵn (v4)Tích hợp sẵnUpstash ConsoleKhông chính thứcKhông
Cloudflare Workers⚠️ chưa xác nhận✅ native
Self-hosted✅ (cần Redis)✅ Docker / K8s✅ (cần Postgres)N/A
Open source✅ MIT✅ MIT✅ Apache-2.0✅ MIT✅ MIT
Free tierN/A (chi phí hạ tầng)20 concurrent run5 concurrent step1,000 msg/ngàyN/A (chi phí hạ tầng)N/A
Giá base~$5–10/tháng (Redis)$50/tháng$75/tháng$1/100K message~$10–20/tháng (Postgres)
Trạng tháiActiveActiveActiveActiveActive⛔ Maintenance

BullMQ v5 — tiêu chuẩn thị trường

5,595,335 lượt tải trong tuần 22–28 tháng 5 năm 2026. Đó là gần 5× lượt tải hàng tuần của Inngest và gần 10× của Trigger.dev. Hiệu ứng ecosystem là có thật: có các integration sẵn có, package cộng đồng, câu trả lời trên Stack Overflow, và những câu chuyện từ production mà bạn có thể tìm lúc 2 giờ sáng khi có sự cố.

BullMQ được viết natively bằng TypeScript — 56.5% codebase. Job payload được type bằng generics:

const queue = new Queue<{ userId: string; type: string }, void>('notifications');

IDE sẽ báo lỗi ngay khi data shape sai, trước khi job được chạy.

Lên lịch cron

BullMQ v5 thay thế API repeat cũ bằng upsertJobScheduler từ v5.16.0. Mọi tutorial trước năm 2023 bạn tìm thấy online đều dùng API cũ. Hãy dùng cách này:

await queue.upsertJobScheduler(
  'daily-report',
  { pattern: '0 15 3 * * *' },
  { name: 'generate-report', data: { type: 'daily' }, opts: { attempts: 3 } },
);

Tham số đầu tiên là scheduler ID, tham số thứ hai là lịch trình (cron pattern hoặc interval), tham số thứ ba là job template. Nhiều scheduler có thể dùng chung một queue.

Retry và concurrency

const worker = new Worker(queueName, processor, {
  concurrency: 50,
});

await queue.add('send-email', { to: '[email protected]' }, {
  attempts: 5,
  backoff: { type: 'exponential', delay: 1000 },
});

Các kiểu backoff: fixed, exponential, hoặc một hàm tùy chỉnh. Concurrency được tính per-worker instance và có thể điều chỉnh lúc runtime (worker.concurrency = 5). Priority là số — số nhỏ hơn nghĩa là ưu tiên cao hơn.

Hạ tầng

BullMQ cần Redis. ioredis v5 là direct production dependency. Một instance Upstash Redis $7/tháng đủ để xử lý workload vừa phải. Railway và Render đều có plan Redis từ khoảng $5/tháng.

Không có Redis thì không có BullMQ. Đó là ràng buộc cứng duy nhất. Nếu bạn đang cân nhắc giữa Redis và Valkey, xem bài so sánh Redis vs Valkey.

Dashboard

Bull Board là dashboard open-source do cộng đồng maintain — @bull-board/api cộng thêm một UI adapter tùy chọn (Express, Fastify, Hapi). Dashboard này không được team BullMQ chính thức maintain nhưng vẫn hoạt động tốt. BullMQ Pro (thương mại, license riêng) có dashboard từ nhà phát hành.

Lưu ý khi nâng cấp từ v4 lên v5

Nếu bạn đang dùng BullMQ v4, thay đổi Job Scheduler API là một rewrite toàn bộ code dùng cron. BullMQ Pro v5 cũng đưa vào các Redis data structure mới cho queue marker. Hãy xem migration guide chính thức trên trang BullMQ trước khi nâng cấp từ v4.


Trigger.dev v4 — managed queue với TypeScript DX

Trigger.dev v4 ra mắt GA vào ngày 2025-08-18. Brief cho bài viết này tham chiếu đến v3, nhưng v4 là phiên bản stable hiện tại (v4.4.6 tại thời điểm viết).

Managed offering chạy trên dedicated VM với warm start (100–300ms so với vài giây khi cold start). Self-hosted là khả thi qua Docker hoặc Kubernetes (Helm chart được thêm vào trong v4), nhưng bạn sẽ mất warm start, auto-scaling, và checkpoint. Hãy tính đến điều này nếu tự host là kế hoạch của bạn.

Giá cả (tính đến 2026-05-30)

Nguồn: trigger.dev/pricing.

PlanThángConcurrent runLưu log
Free$0201 ngày
Hobby$10507 ngày
Pro$50200+30 ngày

Phí per-run trên nền: $0.000025/run ($25 mỗi triệu), cộng thêm compute time theo machine size. Concurrency thêm là $10/tháng cho mỗi 50 run — giảm 5× so với giá trước tháng 11 năm 2025.

Với 1M run/tháng trên plan Pro: $50 base + $25 run = $75 tổng. Với 10M run/tháng: $50 base + $250 run = $300 tổng.

⚠️ Trang trigger.dev/docs/limits vẫn hiển thị con số trước tháng 11. Dùng trang pricing và changelog concurrency làm nguồn chính xác.

Điểm mới trong v4

Waitpoints cho phép một run tạm dừng cho đến khi một điều kiện bên ngoài được thỏa mãn. Các quy trình yêu cầu con người phê duyệt — chờ phản hồi Slack hoặc form submission — trở thành tính năng first-class:

const token = await wait.createToken({ timeout: '24h' });
// gửi token URL đến người dùng để phê duyệt
const result = await wait.forToken<{ approved: boolean }>(token);

Idempotency key ngăn công việc bị thực hiện trùng lặp khi retry. HTTP callback tiếp tục các run đang tạm dừng qua webhook. OpenTelemetry exporter cho phép bạn kết hợp trace của Trigger.dev với Datadog, Honeycomb, hoặc bất kỳ hệ thống nào bạn đang dùng.

Dashboard v4 thực sự hữu ích — navigation theo environment, thống kê per-queue, và trace view hiển thị chính xác job tốn thời gian ở đâu.

Thay đổi không tương thích từ v3 lên v4

Hai điểm quan trọng trong thực tế: custom queue giờ phải được định nghĩa trong code trước khi deploy (không còn dynamic creation), và tham số lifecycle hook thay đổi từ positional sang một object duy nhất ({ payload, ctx }). Team Trigger.dev mô tả việc migration là “thường dưới 5 phút.” Các run v3 tiếp tục xử lý trong quá trình chuyển đổi dual-engine.


Inngest — event-driven, retry theo từng step

Inngest SDK v4 ra mắt GA ngày 2026-03-03. Mô hình này khác với BullMQ hay Trigger.dev: bạn không push job vào queue — bạn định nghĩa các function phản ứng với event. Nền tảng Inngest định tuyến event đến các function của bạn.

const processOrder = inngest.createFunction(
  { id: 'process-order', retries: 5 },
  { event: 'order/created' },
  async ({ event, step }) => {
    const payment = await step.run('charge-card', () =>
      chargeCard(event.data.amount),
    );
    await step.sleep('wait-for-fulfillment', '2d');
    await step.run('send-receipt', () =>
      sendEmail(event.data.email),
    );
  },
);

Điểm khác biệt chính: retry xảy ra theo từng step, không phải theo cả function. Nếu send-receipt thất bại, nó sẽ retry từ step đó — charge-card sẽ không chạy lại. Điều này quan trọng trong các workflow nhiều bước khi các step đầu có side effect.

Giá cả (tính đến 2026-05-30)

Nguồn: inngest.com/pricing.

PlanThángConcurrent stepExecution đi kèm
Hobby$05Xem tại inngest.com/pricing
Pro$75100+1,000,000

Overages: $50 mỗi triệu execution (tối đa 20M). Concurrency thêm: $25 mỗi 25 concurrent step.

Giới hạn execution miễn phí của plan Hobby không thể xác minh từ nguồn chính thức trong quá trình nghiên cứu bài viết này. Hãy kiểm tra inngest.com/pricing trước khi phụ thuộc vào con số cụ thể của free tier.

Với 1M run/tháng trên Pro: $75/tháng cố định. Với 10M run/tháng: $75 base + $450 overage = $525/tháng. Ở quy mô đó, mô hình billing “step” của Inngest khác đáng kể so với mô hình “run” của Trigger.dev — và chúng không thể so sánh trực tiếp.

Khi nào nên dùng Inngest

Nếu workload của bạn tự nhiên theo hướng event-driven (một hành động của user kích hoạt một chuỗi step), mô hình của Inngest sạch hơn vòng lặp queue.add() của BullMQ. Type inference cho event payload trong SDK v4 rất mạnh. Tích hợp với framework rất sâu — Next.js và Vercel được hỗ trợ first-class.

Nếu workload của bạn thiên về batch, BullMQ hoặc Trigger.dev phù hợp hơn.

Cloudflare Workers

Tài liệu deployment của Inngest đề cập Cloudflare Pages. Khả năng tương thích với Workers chưa được xác nhận từ nguồn chính thức. Đừng giả định nó hoạt động trong Cloudflare Worker mà không kiểm tra tại inngest.com/docs.


QStash — lựa chọn duy nhất cho edge

QStash (từ Upstash) không phải là một queue theo nghĩa của BullMQ. Đây là một HTTP message relay. Bạn publish một message qua POST; QStash giao nó đến một HTTP endpoint đã đăng ký. Endpoint của bạn chính là worker.

Mô hình này có một ràng buộc cứng: endpoint của bạn phải có thể truy cập công khai. Không có localhost, không có internal VPC. Nếu worker của bạn chạy sau VPN, QStash không phù hợp.

Những gì nó mang lại: delayed delivery (không giới hạn thời gian trên các plan trả phí), retry tùy chỉnh, lên lịch message, và free tier 1,000 message/ngày. Giá đơn giản — $1 cho 100,000 message theo pay-as-you-go.

Tại sao quan trọng với edge

@upstash/qstash dùng HTTP, không phải TCP socket. Cloudflare Workers không thể mở TCP connection — đó là lý do mọi Redis client dùng ioredis (bao gồm BullMQ) đều không tương thích với Workers. QStash hoạt động vì nó chỉ dùng HTTP.

Tài liệu developer chính thức của Cloudflare liệt kê Upstash là integration first-party. Nếu bạn đang xây dựng pattern giống queue trong Cloudflare Worker, QStash là lựa chọn khả thi duy nhất trong sáu thư viện này. Xem thêm Cloudflare Workers vs Vercel Edge để so sánh tầng hosting.

Không nên đưa ra con số Queue Parallelism cụ thể cho QStash — giới hạn concurrency chính xác không thể xác minh từ nguồn chính thức. Hãy kiểm tra trang pricing của Upstash để biết con số hiện tại.


pg-boss v12 — không cần Redis cho team dùng Postgres

pg-boss đặt job queue của bạn trong cùng database Postgres bạn đang chạy. Không cần Redis. Không cần hạ tầng mới. Nếu bạn dùng Supabase, Neon, hoặc self-hosted Postgres, job tồn tại trong cùng transaction boundary với dữ liệu ứng dụng của bạn.

Version 12 yêu cầu Node 22.12+. Nếu runtime của bạn thấp hơn, đây là rào cản không thể bỏ qua.

Bản phát hành v12 là một rewrite hoàn toàn sang ESM + TypeScript. Thay đổi không tương thích lớn nhất là việc bỏ default export — cú pháp import thay đổi từ import PgBoss from 'pg-boss' thành import { PgBoss } from 'pg-boss'. Các static member trước đây truy cập qua class property (ví dụ PgBoss.getConstructionPlans()) giờ là named export. Tên queue và scheduling key giờ bắt buộc xác thực ký tự (chỉ chấp nhận chữ cái, số, gạch nối, gạch dưới, dấu chấm). Đọc release notes v12 trước khi nâng cấp từ v11.

Lượt tải hàng tuần: 640,137 — nhiều hơn 567,541 của Trigger.dev. Đây không phải là thư viện ngách. Các team chạy stack thuần Postgres đang dùng nó trong production.

Khoảng cách so với BullMQ nằm ở throughput ceiling và ecosystem. Với khối lượng vừa phải, Postgres đủ dùng. Với throughput queue rất cao, Redis có lợi thế về mặt cấu trúc. Không có dashboard chính thức — có các lựa chọn từ cộng đồng nhưng không được project pg-boss maintain.


Quirrel — không nên dùng

1,577 lượt tải mỗi tuần. Đang ở chế độ maintenance từ khoảng tháng 2 năm 2024 (được tác giả xác nhận trong một GitHub discussion). Use case ban đầu — serverless cron cho Vercel và Netlify — giờ đã được native platform features thay thế. Tác giả khuyến nghị BullMQ cho các thay thế self-hosted.

Đề cập đến nó nếu ai đó gửi cho bạn một tutorial từ năm 2022. Ngoài ra, bỏ qua đi.


Khung quyết định

Dùng BullMQ v5 nếu:

  • Bạn đã chạy Redis — chi phí thêm để có queue gần như bằng không
  • Bạn cần ecosystem và cộng đồng lớn nhất (5.6M/tuần, gấp 10× đối thủ tiếp theo)
  • Bạn muốn self-hosted mà không có chi phí mỗi lần thực thi
  • Bạn cần priority queue chi tiết và kiểm soát concurrency trong process
  • Bạn đang dùng Node.js, không phải edge runtime

Dùng Trigger.dev v4 nếu:

  • Bạn muốn không phải quản lý bất kỳ hạ tầng nào
  • Workload của bạn có lưu lượng biến động cao — giá per-run ($0.000025/run) có lợi trong các giai đoạn ít tải
  • Bạn cần waitpoint hoặc quy trình phê duyệt của con người
  • Bạn muốn hỗ trợ Bun runtime (đã xác nhận trong v4)
  • Bạn muốn dashboard first-party mà không cần cài Bull Board
  • Ngân sách: $50/tháng base; 1M run/tháng ≈ $75 tổng

Dùng Inngest nếu:

  • Tư duy của bạn là event-driven: function phản ứng với event, không phải push trực tiếp vào queue
  • Bạn cần retry per-step cho workflow nhiều bước có side effect
  • Bạn đang xây dựng trên Next.js hoặc Vercel và muốn tích hợp sâu với framework
  • Ngân sách: $75/tháng base; bao gồm 1M execution, sau đó $50/triệu

Dùng QStash nếu:

  • Bạn đang dùng Cloudflare Workers hoặc edge runtime khác
  • Bạn cần HTTP-push semantics đến các endpoint có thể truy cập công khai
  • Workload của bạn là fire-and-forget fan-out
  • Bạn không cần in-process worker, priority queue, hoặc trạng thái job phức tạp

Dùng pg-boss v12 nếu:

  • Bạn chạy Postgres và rõ ràng muốn tránh thêm Redis
  • Bạn muốn job trong cùng transaction boundary với dữ liệu ứng dụng
  • Node runtime của bạn là 22.12+
  • Khối lượng queue của bạn vừa phải — Postgres xử lý tốt workload production dưới throughput ceiling mà Redis bắt đầu có lợi thế rõ ràng

Không dùng Quirrel. Đang ở chế độ maintenance. Tác giả đã chuyển sang hướng khác. Bạn cũng nên vậy.


Kết luận

Cho hầu hết backend SaaS production trên Node.js: BullMQ v5. Khoảng cách về mức độ sử dụng là quá rõ ràng để bỏ qua. Redis nhiều khả năng đã có trong stack. Không có chi phí mỗi lần thực thi. Bộ tính năng đầy đủ — cron, retry, delay, priority, concurrency — đều được đáp ứng.

Cho backend managed mới: Trigger.dev v4 so với Inngest có giá tốt hơn ở quy mô tương đương, và tương đương về DX. Chọn Inngest nếu bạn thích mô hình event-driven hoặc đang xây dựng trong hệ sinh thái Next.js / Vercel.

Cho Cloudflare Workers hoặc pure serverless edge: QStash. Không có lựa chọn nào khác trong danh sách này hoạt động được.

Cho team chỉ dùng Postgres: pg-boss v12 — nhưng hãy chấp nhận rằng bạn đang đánh đổi sự đơn giản về hạ tầng lấy quy mô cộng đồng nhỏ hơn (640K/tuần so với 5.6M/tuần của BullMQ) và một migration không nhỏ lên v12 nếu bạn đang từ v11.


Lưu ý

Những gì chúng tôi không test: benchmark throughput thực tế dưới tải — đây là đánh giá thư viện dựa trên tính năng được tài liệu hóa, giá đã xác minh, và dữ liệu lượt tải npm, không phải load test tổng hợp. Các use case khối lượng cao (hàng triệu job/giờ) cần proof-of-concept production trước khi cam kết với bất kỳ thư viện nào.

Free tier của Inngest: giới hạn execution miễn phí chính xác của plan Hobby không thể xác minh từ nguồn chính thức tại thời điểm viết. Hãy kiểm tra inngest.com/pricing trước khi phụ thuộc vào nó.

Concurrency của QStash: giới hạn Queue Parallelism cụ thể không thể xác minh từ nguồn chính thức. Hãy kiểm tra upstash.com/pricing/qstash.

BullMQ v4→v5 migration: các thay đổi Redis data structure trong BullMQ Pro v5 chưa được đặc tả đầy đủ từ nguồn chính thức. Hãy xác minh migration guide chính thức trước khi nâng cấp cài đặt Pro production.

Trigger.dev self-hosted: warm start, auto-scaling, và checkpoint là tính năng chỉ có trên cloud. Một deployment Trigger.dev v4 tự host thiếu cả ba tính năng này.

Thông tin affiliate: bài viết này có chứa affiliate link đến Upstash (QStash). Xem thông tin tiết lộ ở đầu trang.


Tài liệu tham khảo