コンテンツにスキップ

TypeScript クライアント(@dustid/apid-client)

@dustid/apid-client は、DUST API 向けの型付き TypeScript クライアントです。DICE Web アプリケーションが本番環境で使用しているものと同じクライアントで、リクエスト型とレスポンス型は API の OpenAPI 仕様から直接生成されるため、常にサーバーと同期します。

このパッケージは、公開 npm レジストリには公開されていません。DUST が配布しており、連携をご利用のお客様はリクエストに応じて入手できます(support@dustidentity.com までお問い合わせください)。npm install @dustid/apid-client を実行しても見つかりません。

この依存関係を導入しない場合でも、OpenAPI 仕様から型を生成することで同等の型安全性を確保し、通常の fetch で API を呼び出せます。このクライアント自体の型も、まさにこの方法で生成されています。クイックスタートでは、何もインストールせずに実行できるよう、この方法を採用しています。

クライアントの利用を前提に計画する前に、以下を確認してください。

要件詳細
ランタイムグローバルな fetch:Node.js 18 以降、Bun、または Deno。fetcher オプションを使用して、独自の実装(プロキシ、再試行、テストダブル)を注入できます。
TypeScript ツールチェーンこのパッケージのエントリーポイントは、コンパイル済み JavaScript ではなく TypeScript ソースに解決されます。バンドラーまたはランタイムでトランスパイルする必要があります。トランスパイルされていないソースに対して通常の node dist/app.js を実行しても動作しません。
ランタイム依存関係このクライアントは依存関係がないわけではありません。エラーペイロードの検証に使用する arktype と、配布物に含まれる DUST 内部のヘルパーパッケージが依存関係として宣言されています。ベンダリング、ライセンスレビュー、またはエアギャップ環境へのインストールを行う場合は、これらも考慮してください。
実行場所サーバー側のみ — このページの末尾にある注意事項を参照してください。
import { ApidClient } from "@dustid/apid-client";
const client = new ApidClient({
baseUrl: "https://apid.dustid.io",
bearerToken: token, // Authorization: Bearer <token>
organizationId: orgId, // sent as Dust-Ctx-Org-Id
teamId: teamId, // sent as Dust-Ctx-Team-Id
});

完全なオプション型は次のとおりです。

type ApidClientOptions = {
baseUrl: string;
bearerToken?: string;
organizationId?: string; // Dust-Ctx-Org-Id header
teamId?: string; // Dust-Ctx-Team-Id header
fetcher?: typeof globalThis.fetch; // custom fetch (proxies, testing)
defaultHeaders?: HeadersInit | (() => HeadersInit); // e.g. Dust-Ctx-Locale
logger?: Logger; // debug/error request logging
};

organizationId と teamId はコンテキストヘッダーに対応します。teamId を省略すると、サーバーはデフォルトで組織のルートチームを使用します。すべてのリクエストに追加したいものがある場合は、defaultHeaders を使用してください。たとえば、エラーメッセージをローカライズできます。

const client = new ApidClient({
baseUrl,
bearerToken,
organizationId,
defaultHeaders: { "Dust-Ctx-Locale": "zh-CN" },
});

コンテキストまたはトークンの切り替え

Section titled “コンテキストまたはトークンの切り替え”

クライアントはイミュータブルです。2 つのヘルパーは再設定されたコピーを返すため、リクエスト単位またはユーザー単位の範囲指定を低コストで行えます。

const asOtherTeam = client.withContext({ teamId: otherTeamId });
const asFreshToken = client.withToken(newBearerToken);

クライアントはエンドポイントを、client.me、client.threads、client.bundles、client.files、client.tags(識別子の操作 — /api/v1/tags/* エンドポイント)、client.teams、client.sharing、client.templates、client.events、client.relations、client.threadLinks、client.assemblies、client.transfers、client.slices、client.imports、client.fabric、client.users、client.certificates、および client.certificateForms というリソースに分類しています。

メソッド名はリファレンスに対応しています。スレッドを使用した実際の例を 2 つ示します。

// GET /api/v1/threads — cursor-paginated list
const page = await client.threads.list({ pageSize: 50, q: "tire" });
for (const thread of page.threads) {
console.log(thread.threadId, thread.name);
}
if (page.next) {
const nextPage = await client.threads.list({ pageSize: 50, cursor: page.next });
}
// POST /api/v1/threads — create, unwrapped to the single created record
const created = await client.threads.createOne({
type: "single",
thread: { name: "Tire SZ3J-11-ZJ17" },
data: [{ name: "Serial Number", type: "text", value: { text: "SZ3J-11-ZJ17" } }],
});
// GET /api/v1/threads/{thread_id}
const record = await client.threads.get(created.threadId);
console.log(record.thread.name, record.events.length);

threads.create は未加工のバッチレスポンス({ created, uploadResponses })を返します。threads.createOne は created[0] を返す便利なメソッドで、サーバーが何も作成しなかった場合は例外をスローします。

すべてのリクエスト型とレスポンス型(ThreadCreateRequest、ThreadQueryResponse、ThreadGetResponse、…)は、仕様から生成された未加工の paths / components / operations 型とともに、パッケージルートからエクスポートされます。

メソッドは成功時に解析済みの JSON レスポンスを返し、2xx 以外のステータスでは必ず ApiError をスローします。ApiError には、API の標準エラー本文が含まれます。

import { ApiError } from "@dustid/apid-client";
try {
await client.threads.get(threadId);
} catch (error) {
if (error instanceof ApiError) {
// error.code stable error code, e.g. "NOT_FOUND", "UNAUTHORIZED"
// error.status HTTP status number
// error.message localized human-readable message
// error.detail optional extra context (validation issues, etc.)
// error.body the full { code, message, status, detail } payload
if (error.code === "UNAUTHORIZED") {
// token expired — re-exchange the API key and retry
}
} else {
throw error; // network failure or non-JSON response
}
}

把握しておくべき境界的な挙動が 2 つあります。204/205 レスポンスは undefined に解決され、有効な JSON ではないレスポンスでは通常の Error(ApiError ではありません)がスローされます。logger を渡すと、サポートでの照合に使用できる x-request-id とともに、失敗したすべてのリクエストがログに記録されます。

代替手段:独自に型を生成する

Section titled “代替手段:独自に型を生成する”

API は、OpenAPI 3 仕様を https://apid.dustid.io/api/openapi.json で提供しています。openapi-typescript を使用すると、この仕様を完全に型付けされた paths/components 定義に変換でき、通常の fetch または仕様駆動の任意の fetch ラッパーで利用できます。

Terminal window
npx openapi-typescript@7 https://apid.dustid.io/api/openapi.json -o <generated-types-file>
import type { paths } from "./dust-api";
type ThreadList =
paths["/api/v1/threads"]["get"]["responses"]["200"]["content"]["application/json"];

Authorization、Dust-Ctx-Org-Id、および Dust-Ctx-Team-Id ヘッダーは、ご自身で設定してください。リクエスト規約を参照してください。新しい API 機能を取り込むたびに型を再生成してください。/api/openapi.json の仕様は、取得元のサーバーに対して常に最新です。