Node.js-Server-Integration
Zwei Muster für ein Node.js-Backend: Session-Tokens für ein Browser-Frontend erstellen, oder die API direkt aus reinem Server-Code aufrufen.
Node.js auf dem Backend deckt zwei verschiedene Aufgaben ab, beide auf dieser Seite: (1) Session-Tokens erstellen, damit ein Browser-Frontend (einfache Webseite oder React) die API sicher aufrufen kann, und (2) die API direkt aus reinem Server-Code aufrufen — ein Cron-Job, ein Webhook-Handler, eine CLI, alles ohne Browser-Beteiligung. Wählen Sie den Abschnitt, der zu Ihrem Vorhaben passt.
Tokens für ein Browser-Frontend erstellen
Dies ist das Muster aus Wählen Sie Ihren Auth-Modus, konkret umgesetzt: ein Token-Erstellungs-Endpunkt auf Ihrem Server, und eine Browser-Seite (siehe Integration für einfache Webseiten oder React-SPA-Integration für die Browser-Seite), die ihn aufruft, bevor sie das SDK aufruft.
1. Einen Token-Endpunkt zu Ihrem Backend hinzufügen
Der empfohlene Weg, ein Session-JWT zu erstellen, ist, dass Ihr backend den eigenen
POST /auth/token-Endpunkt der Plattform aufruft und sich dabei mit dem
client_id/client_secret-Paar aus Holen Sie sich Ihren API-Schlüssel
authentifiziert. Ihr backend signiert selbst nichts — es leitet die Anfrage nur weiter und gibt das
zurückgegebene Token an den Browser weiter.
const CLIENT_ID = process.env.QURAVIN_CLIENT_ID; // aus der Console
const CLIENT_SECRET = process.env.QURAVIN_CLIENT_SECRET; // aus der Console, einmalige Anzeige
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, // die Nutzer-ID Ihrer Anwendung — wird zum JWT-`sub`
ttl_seconds: 900, // optional; 60-3600, Standard 900 (15 Min)
// pipelines: ["translate-string"], // optional — weglassen für die volle Erlaubnisliste Ihrer Anwendung
}),
});
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 antwortet mit:
{ "token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 900 }
Die von Ihnen angeforderten pipelines werden mit allowed_pipelines Ihrer Anwendung
geschnitten (in der Console festgelegt — siehe Holen Sie sich Ihren
API-Schlüssel); lassen Sie das Feld weg, um alles zu erhalten, was
Ihre Anwendung aufrufen darf. requireUserLogin ist Ihre eigene Auth-Middleware — das Token geht
nur an einen Nutzer, den Ihr Server bereits authentifiziert hat.
Alternative: ein JWT selbst signieren (veraltet)
Manche Anwendungen halten stattdessen ihr eigenes anwendungsbezogenes Signing-Secret
(AI_PLATFORM_SIGNING_SECRET) und erstellen das JWT lokal mit einer
Bibliothek wie jsonwebtoken, anstatt POST /auth/token aufzurufen. Das ist ein echter,
unterstützter Mechanismus, aber der veraltete Pfad — bevorzugen Sie POST /auth/token oben, es
sei denn, Sie haben einen konkreten Grund zum Selbst-Signieren (z. B. Sie arbeiten bereits so und
möchten das nicht ändern).
import jwt from "jsonwebtoken";
const SECRET = process.env.AI_PLATFORM_SIGNING_SECRET; // NICHT das client_secret — siehe unten
const APP_ID = "your-app-id"; // die registrierte ID Ihrer Anwendung
app.get("/ai-token", requireUserLogin, (req, res) => {
const token = jwt.sign(
{
iss: APP_ID,
sub: req.user.id, // die Nutzer-ID Ihrer Anwendung
aud: "ai-platform",
pipelines: ["translate-string"], // welche Pipelines dieser Nutzer aufrufen darf
rate_limit: { rpm: 30 },
},
SECRET,
{ algorithm: "HS256", expiresIn: "15m" },
);
res.json({ token, expires_in: 15 * 60 });
});
Dies verwendet ein anderes Credential als client_id/client_secret. Das Signing-Secret wird
nicht von der Console ausgestellt — es wird außerhalb dieses Flusses vom Plattformbetreiber über
einen sicheren Kanal bereitgestellt, speziell für Anwendungen, die selbst signieren müssen. Wenn
Sie noch keins haben, verwenden Sie stattdessen den POST /auth/token-Pfad oben, anstatt eins
anzufordern.
iss unterscheidet sich zwischen den beiden Erstellungspfaden — gehen Sie nicht davon aus, dass
ein JWT-Payload, den Sie debuggen, immer denselben Aussteller hat. Ein über POST /auth/token
erstelltes Token hat immer iss: "ai-platform" (die Plattform selbst signiert es). Ein selbst
signiertes Token (dieser Abschnitt) hat stattdessen iss: <your-app-id> — die Plattform akzeptiert
es weiterhin, verschlüsselt über das Signing-Secret der registrierten App-ID. Beide verwenden
aud: "ai-platform".
Welches Secret welches ist
Drei verschiedene Secrets tauchen in diesen Auth-Dokumenten auf, und ihre Namen überschneiden sich genug, um Verwechslungen zu verursachen. Dies ist die vollständige Liste:
| Secret | Env-Variable | Wer es besitzt | Wofür verwendet |
|---|---|---|---|
| Client secret | QURAVIN_CLIENT_SECRET (frei benennbar) | Ihr backend | Authorization: Basic client_id:client_secret bei POST /auth/token — der oben empfohlene Pfad. |
| Anwendungsbezogenes Signing-Secret | AI_PLATFORM_SIGNING_SECRET | Ihr backend (nur beim Selbst-Signieren) | JWTs lokal selbst signieren, unter Umgehung von POST /auth/token — der veraltete Pfad in diesem Abschnitt. |
| Platform-JWT-Signing-Secret | (plattforminterne, nicht offengelegt) | Nur der Plattformbetreiber, niemals Sie | Was die Plattform selbst verwendet, um Tokens zu signieren, die sie über POST /auth/token erstellt. Sie sehen oder konfigurieren dies nie. |
Wenn Sie ausschließlich POST /auth/token aufrufen (der empfohlene Pfad), müssen Sie nur an das
Client-Secret denken — die anderen beiden sind interne/veraltete Angelegenheiten.
2. Ihr Frontend holt das Token und ruft das SDK auf
fetch("/ai-token", { credentials: "include" }) ist ein same-origin-Aufruf an Ihre eigene
Backend-Route (Schritt 1) — kein CORS involviert, nur Ihre gewohnte Cookie-basierte Session. Der
eigene Aufruf des SDK an unsere API ist eine separate, Cross-Origin-Anfrage, die das SDK selbst
abwickelt; Sie konfigurieren dafür kein CORS. Der vollständige Browser-seitige Code (einfache
Webseite oder React) befindet sich in Integration für einfache
Webseiten und
React-SPA-Integration — dieser Endpunkt ist das Stück, das diese
beiden Anleitungen als bereits vorhanden voraussetzen.
Das ist der ganze Kreislauf: Ihr Server erstellt Tokens, der Browser sieht nie ein Secret, und jeder Aufruf wird dem Nutzer zugeordnet, der ihn getätigt hat.
Die API direkt aus reinem Server-Code aufrufen
Kein Browser involviert — ein Cron-Job, ein Webhook-Handler, eine CLI, ein Batch-Script.
Verwenden Sie direkt den apiKey-Modus; es gibt kein Token zu erstellen, keinen
Pro-Anfrage-Roundtrip zu POST /auth/token.
Es gibt hier kein window, an das ein <script>-Tag andocken könnte, und die eigene
import { Quravin } from "@quravin/sdk"-Ergonomie des SDK ist noch nicht im öffentlichen
npm-Registry verfügbar (fragen Sie Ihren Plattformbetreiber, falls Sie diesen Pfad benötigen) —
rufen Sie die API für reinen Server-Code auf Node.js heute also direkt über HTTP auf. Node 18+
liefert fetch global mit, keine zusätzliche Abhängigkeit nötig:
const API_BASE = process.env.QURAVIN_API_BASE;
const API_KEY = process.env.QURAVIN_API_KEY; // aus der Console — siehe „Holen Sie sich Ihren API-Schlüssel"
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"
Siehe Direct API integration für die vollständigen Request-/Response-Formen und Fehlertabellen, auf denen diese Schleife aufbaut.
Dies ist derselbe x-api-key aus dem Abschnitt „Was ist mit einem statischen x-api-key?” von
Holen Sie sich Ihren API-Schlüssel — sicher aufzubewahren in reinem
Server-Code (eine Env-Variable, ein Secrets-Speicher), da er nie an einen Browser ausgeliefert
wird. Jeder Aufruf wird der Anwendung zugeordnet, nicht einem bestimmten Nutzer; wenn Sie eine
Zuordnung pro Nutzer auch bei reinem Server-Code benötigen, erstellen Sie stattdessen ein
Session-JWT pro Nutzer (derselbe POST /auth/token-Aufruf wie oben, nur ausgelöst durch Ihre
eigene Serverlogik statt durch eine eingehende Browser-Anfrage — senden Sie es dann als
Authorization: Bearer <jwt> statt als x-api-key).
Fehler behandeln
Ein erreichtes Kontingent oder Rate-Limit gibt einen typisierten Fehler zurück — zeigen Sie ihn passend zu Ihrem Kontext an (eine Nutzermeldung in einem Browser-Flow, ein Retry/Alert in einem Batch-Job):
try {
const out = await runPipeline("translate-string", { text: "Hello", target_language: "de" });
} catch (err) {
console.error("Translation failed:", err.message);
}
Siehe den Errors-Abschnitt der Tool-Referenz für die vollständige Liste der Statuscodes und was jeder davon für den authentifizierten Pfad bedeutet.
Weiterführend
Das SDK unterstützt außerdem einige Muster, die oben nicht gezeigt werden:
ai.runMany(...)— reichen Sie einen Batch von Eingaben ein (z. B. jede Zeile einer Datei) mit begrenzter Nebenläufigkeit und Fortschritt pro Element, statt einesai.run()-Aufrufs pro Eingabe.ai.button(selector, config)— verdrahten Sie einen Button direkt mit einem Pipeline-Aufruf, ohne eigenen Click-Handler, Polling oder Disabled-State-Logik zu schreiben.- Raw-Prompt-Modus — führen Sie einen benutzerdefinierten System-/User-Prompt statt einer benannten Pipeline aus. Siehe Raw-Prompt-Modus für die vollständige Anfrage-/Antwortform, die Modellauswahl und wie er sich von einer benannten Pipeline unterscheidet.
- Troubleshooting-Tabelle — die vollständige Liste der SDK-/HTTP-Fehlerstrings und was jeweils die Ursache ist.
ai.runMany(...), ai.button(...) und die Troubleshooting-Tabelle sind im ausführlicheren
Integration Guide des SDK dokumentiert — er ist nicht öffentlich, fragen Sie also Ihren
Plattformbetreiber nach einer Kopie, falls Sie ihn benötigen.