Workers de file
La queue du SDK livre chaque message au moins une fois. Vous ajoutez des messages dans une file (voir SDK → Queue) et un worker les traite : un handler HTTP de votre application que la plateforme appelle en POST pour chaque message. Chaque application ne voit que ses propres files et ses propres workers.
Réglages d'un worker
Un worker est défini pour une file par les réglages suivants. Le client du framework n'a pas d'appel pour enregistrer un worker : les workers HTTP sont activés pour votre application par la plateforme.
| Champ | Requis | Notes |
|---|---|---|
queue |
oui | Nom de la file à traiter |
handler_url |
oui | L'URL du handler, sur le domaine de votre application |
concurrency |
non | Nombre maximal de livraisons simultanées (1 par défaut, 1 au minimum) |
visibility_timeout_secs |
non | Durée accordée à une livraison avant qu'elle soit relancée (30 par défaut) |
Une file n'a qu'un worker : en définir un nouveau remplace le précédent. Les workers peuvent aussi être listés et retirés.
Un message part en lettre morte après 5 livraisons sans acquittement.
handler_url doit être une URL http ou https avec un nom de domaine (pas d'adresse IP,
pas d'identifiants ni de fragment #), située sous l'une des URL de rappel que la
plateforme a autorisées pour votre application. Une URL mal formée est refusée avec 400 ;
si les workers HTTP ne sont pas activés pour l'application, ou si l'URL n'est pas autorisée,
le refus est un 403. Si l'autorisation est retirée plus tard, les livraisons sont
suspendues sans consommer de tentative.
Exemple de réglages :
{
"queue": "emails",
"handler_url": "https://<app>.inklura.fr/api/queue/emails",
"concurrency": 4,
"visibility_timeout_secs": 60
}
Le contrat du handler
À chaque livraison, la plateforme envoie cette enveloppe JSON en POST à votre handler_url :
{ "id": "<message-id>", "queue": "emails", "payload": { }, "attempts": 0 }
payload porte la valeur passée à push. Avec sdk.queue.push, elle arrive sous forme de
chaîne JSON : décodez-la avec JSON.parse, comme dans l'exemple plus bas. attempts compte les livraisons précédentes :
0 à la première. Les messages d'une file sont pris dans leur ordre d'arrivée, mais avec
une concurrency supérieure à 1 plusieurs livraisons se déroulent en parallèle.
Le statut HTTP de votre réponse décide de la suite :
| Statut de réponse | Suite |
|---|---|
2xx |
Acquitté : le message est terminé et supprimé |
408, 429, 5xx |
Relance : le message est relivré plus tard (jusqu'à 5 livraisons) |
| tout autre statut | Lettre morte : le message part dans la file de lettres mortes, sans relance |
| pas de réponse (injoignable, délai dépassé) | Relance une fois visibility_timeout_secs écoulé |
Sur un statut de relance, un en-tête Retry-After (un nombre entier de secondes) fixe le
délai avant la nouvelle livraison ; sans lui, le délai est de 30 secondes. Dans les deux
cas, il est plafonné au délai de visibilité. Votre handler dispose de
visibility_timeout_secs pour répondre (30 secondes au minimum, 900 au maximum) : réglez-le
au-dessus de la durée d'exécution la plus longue de votre handler, sinon une livraison
lente mais réussie est relancée.
// src/app/api/queue/emails/route.ts
export async function POST(req: Request) {
const { id, payload, attempts } = await req.json();
const data = typeof payload === "string" ? JSON.parse(payload) : payload;
try {
await sendEmail(data);
return new Response("ok"); // 2xx → acquitté
} catch (e) {
if (isTransient(e)) {
return new Response("retry", { // 5xx → relance dans 60 s
status: 503,
headers: { "Retry-After": "60" },
});
}
return new Response("bad payload", { status: 400 }); // 4xx → lettre morte
}
}
La livraison est au moins une fois. Un même message peut arriver plusieurs fois (par
exemple si votre réponse 2xx s'est perdue, ou après un délai de visibilité dépassé). Rendez
vos handlers idempotents en vous appuyant sur l'id du message.
Suivre un job : ajoutez un enregistrement en KV
La queue ne stocke aucun résultat. Une fois un message acquitté, il disparaît : rien ne
permet de savoir ensuite si le job a réussi ni ce qu'il a produit. Si vous avez besoin d'un
statut ou d'un résultat, écrivez votre propre enregistrement, idéalement dans le
KV sous une clé job:<id>.
import { createSdk } from "@bext-stack/framework/sdk";
const sdk = createSdk("<app-id>");
// à l'ajout dans la file (push renvoie l'id du message)
const id = await sdk.queue.push("emails", payload);
await sdk.kv.set(`job:${id}`, { status: "queued", createdAt: Date.now() });
// dans le handler, id vient de l'enveloppe
await sdk.kv.set(`job:${id}`, { status: "running" });
// …travail…
await sdk.kv.set(`job:${id}`, { status: "done", result });
kv.set remplace toute la valeur : reprenez les champs que vous voulez garder à chaque
écriture.
Si vous avez besoin d'un statut et d'une progression intégrés, utilisez plutôt les tasks.
Lettres mortes
Les messages qui épuisent leurs 5 livraisons, ou pour lesquels le handler a renvoyé un statut non relançable, arrivent dans la file de lettres mortes. Ils y restent, avec leur charge utile, jusqu'à ce que vous les relanciez ou les purgiez :
| Opération | Rôle |
|---|---|
| Lister | Consulter les messages en lettre morte (50 par défaut, 500 au plus) avec leur charge utile et leur nombre de tentatives |
| Relancer | Remettre les messages dans la file, compteur de tentatives remis à zéro (toute une file, ou un message par son id) |
| Purger | Supprimer définitivement les messages (toute une file, ou un message par son id) |
Voir aussi
- SDK → Queue : ajout de messages, délais, client
- SDK → Tasks : les jobs longs avec statut et progression
- Tâches planifiées : le travail récurrent