Node.js サーバー統合
Node.js backend 向けの 2 つのパターン: ブラウザフロントエンド用に session token を発行するか、サーバー専用コードから直接 API を呼び出すか。
backend での Node.js は、このページで扱う 2 つの異なる役割をカバーします: (1) ブラウザ フロントエンド(プレーンな Web ページまたは React)が安全に API を呼び出せるよう session token を 発行すること、そして (2) cron job、webhook ハンドラ、CLI など、ブラウザが一切関与しない サーバー専用コードから直接 API を呼び出すことです。作りたいものに合ったセクションを選んでください。
ブラウザフロントエンド向けにトークンを発行する
これは 認証モードを選ぶ のパターンを具体化したものです: サーバー上のトークン発行エンドポイントと、SDK を呼び出す前にそれを呼び出すブラウザページです (ブラウザ側については プレーンな Web ページ統合 または React SPA 統合 を参照してください)。
1. backend にトークンエンドポイントを追加する
session JWT を発行する推奨の方法は、backend がプラットフォーム自身の POST /auth/token
エンドポイントを呼び出すことです。認証には API キーを取得する
で入手した client_id/client_secret のペアを使います。あなたの backend は自分では何も
署名しません — リクエストを転送し、返ってきたトークンをブラウザに渡すだけです。
const CLIENT_ID = process.env.QURAVIN_CLIENT_ID; // コンソールから
const CLIENT_SECRET = process.env.QURAVIN_CLIENT_SECRET; // コンソールから、一度限りの表示
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, // あなたのアプリのユーザー id — JWT の `sub` になる
ttl_seconds: 900, // 任意。60-3600、デフォルト 900(15 分)
// pipelines: ["translate-string"], // 任意 — 省略するとアプリの許可リスト全体になる
}),
});
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 は次のように応答します。
{ "token": "eyJhbGciOi...", "token_type": "Bearer", "expires_in": 900 }
リクエストした pipelines は、あなたの Application の allowed_pipelines(コンソールで設定 —
API キーを取得する 参照)と積集合が取られます。フィールドを
省略すると、あなたのアプリが呼び出せるものすべてが対象になります。requireUserLogin は独自の
認証ミドルウェアです — トークンは、あなたのサーバーがすでに認証済みのユーザーにのみ発行されます。
代替方法: JWT を自己署名する(レガシー)
一部のアプリは、POST /auth/token を呼び出す代わりに、独自のアプリごとの signing secret
(AI_PLATFORM_SIGNING_SECRET)を保持し、jsonwebtoken のようなライブラリでローカルに JWT を
発行します。これは実在するサポート対象の仕組みですが、レガシーな経路です — 自己署名する具体的な
理由(例えばすでにこの方式で運用していて変えたくない、など)がない限り、上記の
POST /auth/token を優先してください。
import jwt from "jsonwebtoken";
const SECRET = process.env.AI_PLATFORM_SIGNING_SECRET; // client_secret ではない — 下記参照
const APP_ID = "your-app-id"; // あなたのアプリの登録 ID
app.get("/ai-token", requireUserLogin, (req, res) => {
const token = jwt.sign(
{
iss: APP_ID,
sub: req.user.id, // あなたのアプリのユーザー ID
aud: "ai-platform",
pipelines: ["translate-string"], // このユーザーが呼び出せるパイプライン
rate_limit: { rpm: 30 },
},
SECRET,
{ algorithm: "HS256", expiresIn: "15m" },
);
res.json({ token, expires_in: 15 * 60 });
});
これは client_id/client_secret とは異なる資格情報を使います。 signing secret は
コンソールから発行されるものではありません — 自己署名が必要なアプリのために、プラットフォームの
運営者が out-of-band で、安全なチャネルを通じて発行するものです。まだ持っていない場合は、それを
申請する代わりに上記の POST /auth/token の経路を使ってください。
2 つの発行経路で iss が異なります — デバッグ中の JWT ペイロードが常に同じ issuer を
持っているとは思わないでください。 POST /auth/token で発行されたトークンは常に
iss: "ai-platform" です(プラットフォーム自身が署名します)。自己署名トークン(この節)は
代わりに iss: <your-app-id> を持ちます — プラットフォームはそれでも受け入れますが、その
アプリ ID に登録された signing secret で検証されます。どちらも aud: "ai-platform" を使います。
どの secret がどれか
これらの認証ドキュメント全体には 3 つの異なる secret が登場し、名前が紛らわしく混同しやすい ため、完全な一覧を示します。
| Secret | Env var | 誰が持つか | 用途 |
|---|---|---|---|
| Client secret | QURAVIN_CLIENT_SECRET(名前は任意) | あなたの backend | POST /auth/token での Authorization: Basic client_id:client_secret — 上記の推奨経路。 |
| アプリごとの signing secret | AI_PLATFORM_SIGNING_SECRET | あなたの backend(自己署名する場合のみ) | POST /auth/token を経由せず、自分でローカルに JWT に署名する — この節のレガシー経路。 |
| プラットフォームの JWT signing secret | (platform-internal, not exposed) | プラットフォーム運営者のみ、あなたは持たない | プラットフォーム自身が POST /auth/token 経由で発行するトークンに署名するために使うもの。あなたがこれを見たり設定したりすることはありません。 |
POST /auth/token(推奨経路)だけを呼び出しているのであれば、意識する必要があるのは client
secret だけです — 残りの 2 つは内部/レガシーな関心事です。
2. フロントエンドがトークンを取得して SDK を呼び出す
fetch("/ai-token", { credentials: "include" }) は、あなた自身の backend のルート(手順 1)への
同一オリジンの呼び出しです — CORS は関与せず、いつも通りの cookie ベースのセッションだけです。
SDK 自身がプラットフォームの API に対して行う呼び出しは、別の、クロスオリジンのリクエストで、
SDK 自身が処理します — これに対して CORS を設定する必要はありません。ブラウザ側の完全な
コード(プレーンな Web ページまたは React)は プレーンな Web ページ統合
と React SPA 統合 に
あります — このエンドポイントは、その 2 つのガイドがすでに存在すると前提しているものです。
これで一連の流れは完成です:サーバーがトークンを発行し、ブラウザは secret を一切見ることなく、 すべての呼び出しは実行したユーザーに紐づけられます。
サーバー専用コードから直接 API を呼び出す
ブラウザは一切関与しません — cron job、webhook ハンドラ、CLI、バッチスクリプトです。API
キーモードを直接使ってください。発行するトークンはなく、POST /auth/token へのリクエストごとの
往復も不要です。
ここには <script> タグを取り付ける window が存在せず、SDK 自身の import { Quravin } from "@quravin/sdk" というエルゴノミクスもまだ公開 npm レジストリには載っていません(その経路が
必要な場合はプラットフォームの運営者にお問い合わせください) — そのため、今日のサーバー専用の
Node.js コードでは、API を HTTP 経由で直接呼び出します。Node 18+ ではグローバルに fetch が
使えるため、追加の依存関係は不要です。
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"
このループの土台になっている完全なリクエスト/レスポンス形式とエラー一覧については、 Direct API integration を参照してください。
これは API キーを取得する の「静的な x-api-key については?」
セクションと同じ x-api-key です — ブラウザに渡ることが決してないため、サーバー専用のコード
(env var、secrets manager)に保管しても安全です。すべての呼び出しは特定のユーザーではなく
アプリに紐づけられます。サーバー専用のコードからでもユーザーごとの帰属が必要な場合は、代わりに
ユーザーごとに session JWT を発行してください(上記と同じ POST /auth/token 呼び出しを、
受信したブラウザリクエストではなく自分のサーバーロジックからトリガーするだけです — その場合は
x-api-key の代わりに Authorization: Bearer <jwt> として送ってください)。
エラーを処理する
クォータや rate limit に達すると、型付きエラーが返ります — 自分の状況に合わせて表示してください (ブラウザのフローではユーザー向けメッセージ、バッチジョブではリトライ/アラートなど)。
try {
const out = await runPipeline("translate-string", { text: "Hello", target_language: "de" });
} catch (err) {
console.error("Translation failed:", err.message);
}
認証済みの経路が返しうるステータスコードの完全な一覧については、ツールリファレンス の Errors セクションを参照してください。
さらに深く知りたい場合
SDK には、上記に載っていないパターンもいくつかサポートされています。
ai.runMany(...)— 入力ごとにai.run()を 1 回ずつ呼ぶ代わりに、入力のバッチ(例えば ファイルの各行)を、並行数の上限と項目ごとの進捗付きで送信します。ai.button(selector, config)— 自分でクリックハンドラ、ポーリング、disabled 状態の ロジックを書かずに、ボタンを直接パイプライン呼び出しに結び付けます。- Raw-prompt モード — 名前付きのパイプラインの代わりに、カスタムの system/user プロンプトを 実行します。完全なリクエスト/レスポンスの形、モデル選択、名前付きパイプラインとの違いに ついては Raw prompt mode を参照してください。
- トラブルシューティング表 — SDK/HTTP のエラー文字列とその原因の完全な一覧です。
ai.runMany(...)、ai.button(...)、トラブルシューティング表は SDK のより詳しい
Integration Guide に記載されています — 公開されていないため、必要な場合はプラットフォームの
運営者にコピーを依頼してください。