#rust (3)
問題
REST APIとRust CLIを別々に開発すると、レスポンスのJSON構造が噛み合わなくなる。
典型的な不一致パターン:
| API が返す | CLI が期待する | エラー |
|---|---|---|
[{...}] (配列直接) | { "items": [{...}] } (ラップ) | invalid type: map, expected a sequence |
{ "success": true } | { "message": "..." } | missing field 'message' |
{ "id", "name", "extra_field" } | { "id", "name" } (フィールド不足) | missing field 'extra_field' ではなく別のエラー |
対策
- APIレスポンスは常にオブジェクトでラップする(
{ tasks: [...] }形式)。拡張性も高い - Rust側のデシリアライズ構造体は、APIが返す全フィールドを網羅するか、
#[serde(deny_unknown_fields)]を付けない(デフォルトで未知フィールドは無視される) - API仕様を先に決めてから両方実装する。OpenAPI等で契約を明文化するのがベスト
開発中のCLI実行方法
ビルドしたバイナリはプロジェクト内に生成されるため、PATHが通っていない。 実行するには毎回フルパスを指定するか、手動でPATHに通す必要がある。
# 方法1: シンボリックリンクでPATHの通った場所に配置
ln -s $(pwd)/target/release/my-cli /usr/local/bin/my-cli
# 方法2: cargo install(~/.cargo/bin/ に配置される)
cargo install --path .
リリース後の配布
ユーザーはパッケージマネージャ経由でインストールする。内部的にやっていることは同じ(PATHが通った場所にバイナリを置く)。
cargo install my-cli # crates.io
brew install my-cli # Homebrew
npm install -g my-cli # npm (Node製CLIの場合)
ポイント
npm i -gやcargo installが「魔法」に見えるが、本質は PATHの通ったディレクトリにバイナリを配置する だけ- 開発中は手動で同じことをしているに過ぎない
ln -sはショートカット作成。実体はビルドディレクトリにあるまま
気づき
同一プロジェクトでコンパイル言語(Rust CLI)とスクリプト言語(TypeScript API)を併用していると、修正の反映タイミングが異なることを忘れがち。
| 言語 | 反映方法 | 理由 |
|---|---|---|
| TypeScript (dev server) | 自動反映(ホットリロード) | devサーバーがファイル変更を検知して再起動 |
| Rust (CLI) | 手動リビルド必須 | コンパイル言語なのでバイナリを再生成する必要がある |
実用的な注意点
- API側だけの修正ならリビルド不要で即確認できる
- CLI側の修正は
cargo build --releaseが必要 - 両方修正した場合、API側は即反映されるがCLI側のエラーが残って混乱しやすい