コンテンツにスキップ

TypeBox: プロトコル定義の唯一の真実(Source of Truth)として活用する

複数のプラットフォームで動作するアプリケーションを開発していると、サーバーとクライアント間での通信定義の同期に苦労することがよくあります。API の仕様が変わるたびに、サーバーのバリデーション、ドキュメント、そして各言語のモデルを手動で更新するのは、ミスの原因になります。

通信プロトコルの定義がバラバラに存在していると、型の不一致によるバグが発生しやすくなります。これを解決するために、私たちは TypeBox を「唯一の真実(Source of Truth)」として使い、すべてのコードを一つの定義から生成しています。

この仕組みを利用するには、以下のツールと環境が必要です。

  • TypeBox
  • TypeScript
  • AJV (ランタイムバリデーション用)
  • pnpm
  • Node.js

5 分でプロトコルの管理フローを理解しましょう。

  1. スキーマを定義する: src/gateway/protocol/schema.ts で TypeBox を使って型を定義します。
  2. コードを生成する: 以下のコマンドを実行して、JSON Schema と Swift モデルを更新します。
    Terminal window
    pnpm protocol:gen
    pnpm protocol:gen:swift
  3. 整合性を確認する: CI などで生成物が最新であることを確認します。
    Terminal window
    pnpm protocol:check

Gateway WebSocket のメッセージは、以下の 3 つのフレームのいずれかに分類されます。

  • Request: { type: "req", id, method, params }
  • Response: { type: "res", id, ok, payload | error }
  • Event: { type: "event", event, payload, seq?, stateVersion? }

最初のフレームは必ず connect リクエストである必要があります。その後にメソッドの呼び出しやイベントの購読が可能になります。

Client Gateway
|---- req:connect -------->|
|<---- res:hello-ok --------|
|<---- event:tick ----------|
|---- req:health ---------->|
|<---- res:health ----------|

定義されたスキーマは、以下の場所で直接利用されます。

  • サーバー側: AJV を使用して、受信したすべてのフレームを検証します。connect リクエストのパラメータが ConnectParams と一致するかをチェックします。
  • クライアント側: JS クライアントは、受信したイベントやレスポンスフレームを使用前に検証します。
  • メソッドの公開: Gateway は hello-ok レスポンス内で、サポートしている methods と events を通知します。

Node.js による最小構成のクライアント例

Section titled “Node.js による最小構成のクライアント例”

以下は、接続してから health メソッドを呼び出す最小限の実装です。

import { WebSocket } from "ws";
const ws = new WebSocket("ws://127.0.0.1:18789");
ws.on("open", () => {
ws.send(
JSON.stringify({
type: "req",
id: "c1",
method: "connect",
params: {
minProtocol: 3,
maxProtocol: 3,
client: {
id: "cli",
displayName: "example",
version: "dev",
platform: "node",
mode: "cli",
},
},
}),
);
});
ws.on("message", (data) => {
const msg = JSON.parse(String(data));
if (msg.type === "res" && msg.id === "c1" && msg.ok) {
ws.send(JSON.stringify({ type: "req", id: "h1", method: "health" }));
}
if (msg.type === "res" && msg.id === "h1") {
console.log("health:", msg.payload);
ws.close();
}
});

実践例:新しいメソッドの追加

Section titled “実践例:新しいメソッドの追加”

例として、system.echo メソッドを追加する手順を紹介します。

  1. スキーマの定義: src/gateway/protocol/schema.ts に追加します。
export const SystemEchoParamsSchema = Type.Object(
{ text: NonEmptyString },
{ additionalProperties: false },
);
export const SystemEchoResultSchema = Type.Object(
{ ok: Type.Boolean(), text: NonEmptyString },
{ additionalProperties: false },
);
  1. バリデータの書き出し: src/gateway/protocol/index.ts で AJV バリデータを作成します。
export const validateSystemEchoParams = ajv.compile<SystemEchoParams>(SystemEchoParamsSchema);
  1. サーバーの実装: src/gateway/server-methods/system.ts にハンドラーを追加します。
export const systemHandlers: GatewayRequestHandlers = {
"system.echo": ({ params, respond }) => {
const text = String(params.text ?? "");
respond(true, { ok: true, text });
},
};
  1. コードの再生成:
Terminal window
pnpm protocol:check

プロトコルバージョンの不一致

Section titled “プロトコルバージョンの不一致”

クライアントが送信する minProtocol と maxProtocol がサーバーの対応範囲外の場合、サーバーは接続を拒否します。src/gateway/protocol/schema.ts の PROTOCOL_VERSION を確認してください。

Swift モデルでは、前方互換性を保つために未知のフレームタイプを raw payload として保持します。古いクライアントが新しいメッセージを受信しても、クラッシュせずに処理を継続できます。

  • Gateway architecture: Gateway の全体像を学ぶ
  • dist/protocol.schema.json: 生成された JSON Schema を確認する

詳細な設定や個別のケースについては AI Setup Assistant を活用してください。

OpenClaw

OpenClaw Expert

まだ解決しませんか?

このページで解決しない場合は、OpenClaw Expertに直接質問してください。