httpAction et ctx.db : pourquoi ça ne marche pas

2 min read29 avril 2026#convex#webhook#httpaction#backend

httpAction et ctx.db : pourquoi ça ne marche pas

Tu reçois un webhook — Moneroo, Stripe, peu importe. Tu veux enregistrer l'événement direct en base. Logique, non ?

// Ce qu'on voudrait faire
http.route({
  path: "/webhooks/payment",
  method: "POST",
  handler: httpAction(async (ctx, req) => {
    const data = await req.json();
    await ctx.db.insert("events", data); // ❌ Erreur
  })
});

Ça plante. Et le message d'erreur n'est pas très clair la première fois.


Le pourquoi : un httpAction vit en dehors du système transactionnel de Convex. Il peut recevoir des requêtes HTTP, lire les headers, parser le body — mais il ne peut pas écrire directement en base.

Pour écrire, il doit passer par une mutation.

http.route({
  path: "/webhooks/payment",
  method: "POST",
  handler: httpAction(async (ctx, req) => {
    const rawBody = await req.text();
 
    // Vérifier la signature d'abord
    const signature = req.headers.get("x-moneroo-signature") ?? "";
    if (!verifySignature(rawBody, signature)) {
      return new Response("Unauthorized", { status: 401 });
    }
 
    // Déléguer l'écriture à une mutation
    await ctx.runMutation(internal.webhooks.handle, JSON.parse(rawBody));
    return new Response("OK", { status: 200 });
  })
});

La répartition des rôles : — httpAction : reçoit, vérifie, transmet — mutation : écrit en base, de façon transactionnelle

Vérifie toujours la signature avant de runMutation. Si tu délègues d'abord et vérifies après, tu as déjà écrit des données non vérifiées en base.