← 開発者ガイド

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 secretAI_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 が登場し、名前が紛らわしく混同しやすい ため、完全な一覧を示します。

SecretEnv var誰が持つか用途
Client secretQURAVIN_CLIENT_SECRET(名前は任意)あなたの backendPOST /auth/token での Authorization: Basic client_id:client_secret — 上記の推奨経路。
アプリごとの signing secretAI_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.button(...)、トラブルシューティング表は SDK のより詳しい Integration Guide に記載されています — 公開されていないため、必要な場合はプラットフォームの 運営者にコピーを依頼してください。