Skip to main content

Introduction

Gérer des tâches asynchrones, c’est indispensable dès que votre application commence à faire autre chose que répondre à des requêtes HTTP.
Quand un utilisateur déclenche une action – inscription, achat, demande de document – il s’attend à une réponse immédiate. Sauf que certaines opérations sont lourdes : génération de PDF, envoi d’emails, appels à des APIs externes, traitement d’images
Si vous faites ça dans le thread principal Node.js, vous ralentissez tout : l’utilisateur attend, votre serveur traite moins de requêtes et l’expérience se dégrade.
La solution : déléguer ces tâches secondaires à un worker qui les exécute en arrière-plan pendant que votre API répond immédiatement.

Pourquoi exécuter des tâches en arrière-plan ?

Utilisateur → API (réponse en 50ms) → "Votre reçu sera envoyé par email"
                    ↓
              Job en queue
                    ↓
        Worker (traite en 5 secondes) → Email envoyé
Résultat : votre API reste rapide, et vos tâches lourdes sont exécutées sans impacter l’UX.

Le paysage des solutions de queues

 

Solution Type Avantages Inconvénients
Redis + Bull / BullMQ In-memory Très rapide, très populaire Infrastructure supplémentaire, perte de données possible
RabbitMQ Message broker Robuste, routing avancé Complexe, courbe d’apprentissage
Amazon SQS Cloud managed Scalable, entièrement managé Vendor lock-in, coûts variables
Kafka Event streaming Très haute performance, replay Overkill pour la majorité des projets
PgBoss PostgreSQL-based Simple, transactionnel, même base de données Moins performant que Redis

Pour 90% des projets, PgBoss est le choix le plus pragmatique :
pas d’infrastructure supplémentaire, persistance garantie et performances suffisantes pour des milliers de jobs par jour.

Vocabulaire : Queue, Job, Worker, Cron

Terme Définition Exemple
Queue File d’attente nommée generate-invoice, send-email
Job Tâche unitaire dans une queue Générer la facture #12345
Worker Processus qui consomme les jobs Script Node.js dédié
Schedule / Cron Job récurrent programmé Nettoyage tous les jours à 03:00

Exemple concret : génération de reçus fiscaux

Côté API : envoyer un job

"`typescript"`
// Endpoint de don
app.post('/api/donations', async (req, res) => {
  const donation = await donationRepository.create(req.body);

  // Enqueue le job de génération (non bloquant)
  await boss.send('generate-tax-receipt', {
    donationId: donation.id,
  });

  // Réponse immédiate à l'utilisateur
  res.status(201).json({
    message: 'Don enregistré, votre reçu fiscal arrive par email'
  });
});
L’utilisateur reçoit sa réponse en quelques millisecondes. La génération du PDF (qui peut prendre plusieurs secondes) se fera en arrière-plan.

Côté Worker : traiter les jobs

"`typescript"`
// worker.ts - processus séparé
import PgBoss from 'pg-boss';

const boss = new PgBoss(process.env.DATABASE_URL);
await boss.start();

// Écoute la queue 'generate-tax-receipt'
await boss.work('generate-tax-receipt', async (job) => {
  const { donationId } = job.data;

  console.log(`Génération du reçu pour donation ${donationId}...`);

  // Logique lourde ici
  const pdf = await generatePdfReceipt(donationId);
  await uploadToStorage(pdf);
  await sendEmailWithAttachment(donationId, pdf);

  console.log(`Reçu envoyé pour donation ${donationId}`);
});

console.log('Worker démarré, en attente de jobs...');

Lancer le worker

# Terminal 1 : API
node api.js

# Terminal 2 : Worker
node worker.js
[/vc_column_text]

Jobs récurrents : Cron et Schedules

PgBoss gère nativement les tâches planifiées, comme un cron système, mais avec l’avantage de la persistance en base.
"`typescript"`
// Nettoyage quotidien des tokens expirés
await boss.schedule('clear-expired-tokens', '0 3 * * *', {
  // Données optionnelles
});

// Worker pour ce cron
await boss.work('clear-expired-tokens', async () => {
  const deleted = await tokenRepository.deleteExpired();
  console.log(`${deleted} tokens expirés supprimés`);
});
La syntaxe cron standard s’applique :
- 0 3 * * *  → tous les jours à 3h00
- */15 * * * * → toutes les 15 minutes
- 0 0 * * 0 → chaque dimanche à minuit

Monitoring avec pg-bossman

Surveiller vos jobs est essentiel en production. pg-bossman est un dashboard web qui s’intègre à votre application Express/Hono.

Installation

npm install pg-bossman

Intégration Express

"`typescript"`
import express from 'express';
import { PgBossman } from 'pg-bossman';

const app = express();

// Dashboard accessible sur /admin/queues
app.use('/admin/queues', PgBossman({
  connectionString: process.env.DATABASE_URL,
}));

Sécurisation avec Basic Auth

"`typescript"`
import basicAuth from 'express-basic-auth';

app.use('/admin/queues',
  basicAuth({
    users: { 'admin': process.env.PGBOSSMAN_PASSWORD },
    challenge: true,
  }),
  PgBossman({ connectionString: process.env.DATABASE_URL })
);
Le dashboard affiche :
  • Jobs en attente, actifs, complétés, échoués
  • Statistiques par queue
  • Possibilité de retry les jobs en erreur
  • Historique des exécutions

Gestion des erreurs et retry

PgBoss gère automatiquement les échecs avec un système de retry configurable :
"`typescript"`
await boss.work('send-email', async (job) => {
  try {
    await emailService.send(job.data);
  } catch (error) {
    // Log l'erreur, le job sera retry automatiquement
    console.error(`Échec envoi email: ${error.message}`);
    throw error; // Important : relancer pour déclencher le retry
  }
});

Avantages de PgBoss vs Redis/RabbitMQ

1. Même base de données = Transactions ACID

"`typescript"`
await db.transaction(async (trx) => {
  const order = await trx.insert('orders', orderData);

  // Le job est créé dans la MÊME transaction
  await boss.send('process-order', { orderId: order.id });

  // Si la transaction échoue, le job n'existe pas
});
Avec Redis, vous risquez d’avoir un job orphelin si l’insertion échoue.

2. Pas d’infrastructure supplémentaire

  • Pas de Redis à installer, configurer, monitorer
  • Pas de RabbitMQ avec ses exchanges et sa complexité
  • Pas de coûts cloud supplémentaires

3. Persistance garantie

Les jobs survivent aux redémarrages. Pas de risque de perte de données comme avec Redis sans persistance.

4. Déploiement simplifié

# docker-compose.yml
services:
  postgres:
    image: postgres:16

  api:
    build: .
    command: node api.js

  worker:
    build: .
    command: node worker.js
Deux containers Node.js, une base PostgreSQL. C’est tout.

Quand NE PAS utiliser PgBoss ?

  • Très haute fréquence : > 10 000 jobs/seconde → préférez Redis/Kafka
  • Routing complexe : patterns pub/sub avancés → RabbitMQ
  • Event sourcing : replay d’événements → Kafka
Pour la majorité des applications web (e-commerce, SaaS, applications métier), PgBoss est largement suffisant et beaucoup plus simple à opérer.

Conclusion

PgBoss transforme votre PostgreSQL en système de queue robuste sans ajouter de complexité à votre stack. En 10 minutes, vous pouvez :
  • Installer PgBoss
  • Envoyer vos premiers jobs depuis l’API
  • Créer un worker qui les traite
  • Monitorer le tout avec pg-bossman
Plus besoin de bloquer vos utilisateurs pendant la génération de PDF, l’envoi d’emails ou les appels API lents. Déléguez ces tâches lourdes à vos workers et offrez une expérience utilisateur fluide.