← Guides pour développeurs

Intégration serveur Node.js

Deux schémas pour un backend Node.js : créer des session tokens pour un frontend navigateur, ou appeler l'API directement depuis du code serveur uniquement.

Node.js côté backend couvre deux tâches différentes, toutes deux traitées sur cette page : (1) créer des session tokens pour qu’un frontend navigateur (page simple ou React) puisse appeler l’API en toute sécurité, et (2) appeler l’API directement depuis du code serveur uniquement — un cron job, un gestionnaire de webhook, un CLI, tout ce qui n’implique aucun navigateur. Choisissez la section qui correspond à ce que vous construisez.

Créer des tokens pour un frontend navigateur

Voici concrètement le schéma de Choisissez votre mode d’authentification : un endpoint de création de token sur votre serveur, et une page de navigateur (voir Intégration directe d’une page web simple ou Intégration React SPA pour le côté navigateur) qui l’appelle avant d’appeler le SDK.

1. Ajoutez un endpoint de token à votre backend

La façon recommandée de créer un session JWT est de faire appeler par votre backend l’endpoint POST /auth/token de la plateforme elle-même, en s’authentifiant avec la paire client_id/client_secret obtenue dans Obtenez votre clé API. Votre backend ne signe jamais rien lui-même — il transmet simplement la requête et remet le token retourné au navigateur.

const CLIENT_ID = process.env.QURAVIN_CLIENT_ID;         // from the Console
const CLIENT_SECRET = process.env.QURAVIN_CLIENT_SECRET; // from the Console, one-time reveal

app.get("/ai-token", requireUserLogin, async (req, res) => {
  const basic = Buffer.from(`${CLIENT_ID}:${CLIENT_SECRET}`).toString("base64");

  const r = await fetch("https://api.quravin.com/auth/token", {
    method: "POST",
    headers: {
      Authorization: `Basic ${basic}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      subject: req.user.id,     // your app's user id — becomes the JWT `sub`
      ttl_seconds: 900,         // optional; 60-3600, default 900 (15 min)
      // pipelines: ["translate-string"],  // optional — omit to get your app's full allow-list
    }),
  });
  if (!r.ok) return res.status(502).json({ error: "token_mint_failed" });

  const { token, expires_in } = await r.json();
  res.json({ token, expires_in });
});

POST /auth/token répond avec :

{ "token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 900 }

Les pipelines que vous demandez sont croisés avec le allowed_pipelines de votre Application (défini dans la Console — voir Obtenez votre clé API) ; omettez le champ pour obtenir tout ce que votre application est autorisée à appeler. requireUserLogin est votre propre middleware d’authentification — le token n’est délivré qu’à un utilisateur que votre serveur a déjà authentifié.

Alternative : auto-signer un JWT (hérité)

Certaines applications préfèrent conserver leur propre signing secret propre à l’application (AI_PLATFORM_SIGNING_SECRET) et créer le JWT localement avec une bibliothèque comme jsonwebtoken, au lieu d’appeler POST /auth/token. C’est un mécanisme réel et pris en charge, mais c’est le chemin hérité — préférez POST /auth/token ci-dessus, sauf si vous avez une raison précise d’auto-signer (par exemple vous fonctionnez déjà ainsi et ne voulez pas en changer).

import jwt from "jsonwebtoken";

const SECRET = process.env.AI_PLATFORM_SIGNING_SECRET;   // NOT the client_secret — see below
const APP_ID = "your-app-id";                             // your app's registered id

app.get("/ai-token", requireUserLogin, (req, res) => {
  const token = jwt.sign(
    {
      iss: APP_ID,
      sub: req.user.id,                    // your app's user id
      aud: "ai-platform",
      pipelines: ["translate-string"],     // which pipelines this user may call
      rate_limit: { rpm: 30 },
    },
    SECRET,
    { algorithm: "HS256", expiresIn: "15m" },
  );
  res.json({ token, expires_in: 15 * 60 });
});

Cela utilise un identifiant différent de client_id/client_secret. Le signing secret n’est pas délivré par la Console — il est fourni hors bande par l’opérateur de la plateforme, via un canal sécurisé, spécifiquement pour les applications qui ont besoin d’auto-signer. Si vous n’en avez pas déjà un, utilisez le chemin POST /auth/token ci-dessus plutôt que d’en demander un.

iss diffère entre les deux chemins de création — ne supposez pas qu’un payload de JWT que vous déboguez a toujours le même émetteur. Un token créé via POST /auth/token a toujours iss: "ai-platform" (la plateforme elle-même le signe). Un token auto-signé (cette section) a à la place iss: <your-app-id> — la plateforme l’accepte quand même, en fonction du signing secret enregistré pour cet id d’application. Les deux utilisent aud: "ai-platform".

Quel secret est lequel

Trois secrets différents apparaissent dans ces docs d’authentification, et leurs noms se ressemblent assez pour causer des confusions. Voici la liste complète :

SecretVariable d’envQui le détientUtilisé pour
Client secretQURAVIN_CLIENT_SECRET (nommez-le comme vous voulez)Votre backendAuthorization: Basic client_id:client_secret sur POST /auth/token — le chemin recommandé ci-dessus.
Signing secret propre à l’applicationAI_PLATFORM_SIGNING_SECRETVotre backend (seulement si vous auto-signez)Signer localement vos propres JWT, en contournant POST /auth/token — le chemin hérité de cette section.
Platform JWT signing secret(platform-internal, not exposed)L’opérateur de la plateforme uniquement, jamais vousCe que la plateforme elle-même utilise pour signer les tokens qu’elle crée via POST /auth/token. Vous ne le voyez ni ne le configurez jamais.

Si vous n’appelez jamais que POST /auth/token (le chemin recommandé), seul le client secret vous concerne — les deux autres sont des préoccupations internes/héritées.

2. Votre frontend récupère le token et appelle le SDK

fetch("/ai-token", { credentials: "include" }) est un appel de même origine vers votre propre route backend (étape 1) — aucun CORS impliqué, juste votre session habituelle basée sur les cookies. L’appel du SDK lui-même à notre API est une requête séparée, cross-origin, gérée par le SDK lui-même ; vous n’avez pas à configurer de CORS pour elle. Le code complet côté navigateur (page simple ou React) se trouve dans Intégration directe d’une page web simple et Intégration React SPA — cet endpoint est l’élément que ces deux guides supposent déjà en place.

Voilà toute la boucle : votre serveur crée les tokens, le navigateur ne voit jamais de secret, et chaque appel est attribué à l’utilisateur qui l’a effectué.

Appeler l’API directement depuis du code serveur uniquement

Aucun navigateur impliqué — un cron job, un gestionnaire de webhook, un CLI, un script de batch. Utilisez le mode clé API directement ; il n’y a aucun token à créer, aucun aller-retour par requête vers POST /auth/token.

Il n’y a aucun window ici auquel une balise <script> pourrait s’attacher, et l’ergonomie import { Quravin } from "@quravin/sdk" du SDK lui-même n’est pas encore sur le registre npm public (demandez à l’opérateur de la plateforme si vous avez besoin de ce chemin) — donc pour du code Node.js serveur uniquement aujourd’hui, appelez l’API directement en HTTP. Node 18+ fournit fetch globalement, sans dépendance supplémentaire :

const API_BASE = process.env.QURAVIN_API_BASE;
const API_KEY = process.env.QURAVIN_API_KEY; // from the Console — see "Get your API key"

async function runPipeline(pipelineId, inputs) {
  const submit = await fetch(`${API_BASE}/tickets`, {
    method: "POST",
    headers: { "Content-Type": "application/json", "x-api-key": API_KEY },
    body: JSON.stringify({ mode: "pipeline", pipeline_id: pipelineId, inputs }),
  });
  const { ticket_id } = await submit.json();

  while (true) {
    await new Promise((r) => setTimeout(r, 1000));
    const poll = await fetch(`${API_BASE}/tickets/${ticket_id}`, {
      headers: { "x-api-key": API_KEY },
    });
    const ticket = await poll.json();
    if (ticket.status === "DONE") return ticket.result;
    if (ticket.status === "FAILED") throw new Error(ticket.error);
  }
}

const out = await runPipeline("translate-string", { text: "Hello", target_language: "de" });
console.log(out.translation); // "Hallo"

Voir Intégration API directe pour les schémas complets de requête/réponse et les tableaux d’erreurs sur lesquels cette boucle s’appuie.

C’est la même x-api-key que dans la section « Qu’en est-il d’une x-api-key statique ? » d’Obtenez votre clé API — sûre à conserver dans du code serveur uniquement (une variable d’environnement, un gestionnaire de secrets) puisqu’elle n’atteint jamais un navigateur. Chaque appel est attribué à l’application, pas à un utilisateur particulier ; si vous avez besoin d’une attribution par utilisateur même depuis du code serveur uniquement, créez plutôt un session JWT par utilisateur (le même appel POST /auth/token que ci-dessus, simplement déclenché par la logique de votre propre serveur plutôt que par une requête entrante du navigateur — envoyez-le alors comme Authorization: Bearer <jwt> au lieu de x-api-key).

Gérer les erreurs

Un quota ou une limite de débit atteint retourne une erreur typée — affichez-la comme il convient à votre contexte (un message utilisateur dans un flux navigateur, une nouvelle tentative/alerte dans un job batch) :

try {
  const out = await runPipeline("translate-string", { text: "Hello", target_language: "de" });
} catch (err) {
  console.error("Translation failed:", err.message);
}

Voir la section Errors de Référence des outils pour la liste complète des codes de statut et ce que chacun signifie pour le chemin authentifié.

Pour aller plus loin

Le SDK prend aussi en charge quelques schémas non montrés ci-dessus :

ai.runMany(...), ai.button(...), et le tableau de dépannage sont documentés dans le guide d’intégration complet du SDK — il n’est pas public, demandez-en donc une copie à l’opérateur de la plateforme si vous en avez besoin.