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 :
| Secret | Variable d’env | Qui le détient | Utilisé pour |
|---|---|---|---|
| Client secret | QURAVIN_CLIENT_SECRET (nommez-le comme vous voulez) | Votre backend | Authorization: Basic client_id:client_secret sur POST /auth/token — le chemin recommandé ci-dessus. |
| Signing secret propre à l’application | AI_PLATFORM_SIGNING_SECRET | Votre 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 vous | Ce 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(...)— soumettez un lot d’entrées (par exemple chaque ligne d’un fichier) avec une concurrence bornée et une progression par élément, au lieu d’un appelai.run()par entrée.ai.button(selector, config)— reliez un bouton directement à un appel de pipeline sans écrire votre propre gestionnaire de clic, votre polling, ni votre logique d’état désactivé.- Mode raw-prompt — exécutez un prompt système/utilisateur personnalisé au lieu d’un pipeline nommé. Voir Mode raw prompt pour la forme complète de la requête/réponse, le choix du modèle, et en quoi il diffère d’un pipeline nommé.
- Tableau de dépannage — la liste complète des messages d’erreur SDK/HTTP et de ce qui cause chacun.
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.