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.
