この記事は、API通信を 基礎知識ゼロから実務で通用するところまで 学ぶシリーズ(全4回)の 第4回 です。同じ「ToDo API」を REST・gRPC・Connect の3方式で作りながら、仕組み・実装・実務での運用を順に解説します。
| 回 | 内容 | 章 |
|---|---|---|
| 第1回 | Web通信の基礎とREST | 00〜03 |
| 第2回 | gRPCとProtocol Buffers | 04〜06 |
| 第3回 | Connect | 07〜09 |
| 第4回 | 技術選定とミドルへのステップアップ | 10〜11・用語集 |
はじめに
最終回では、ここまで学んだ REST・gRPC・Connect を並べて比較し、技術選定の考え方を整理します。そのうえで、APIを扱うエンジニアがジュニアからミドルに上がるときに求められる視点・演習課題・チェックリストをまとめます。
10 比較と技術選定
3つの方式に優劣はなく、向き不向きがあります。技術選定の場で根拠を持って意見を言えるように整理します。
| 観点 | REST | gRPC | Connect |
|---|---|---|---|
| 設計の単位 | リソース(名詞)+HTTPメソッド | 関数(RPC) | 関数(RPC) |
| 契約(スキーマ) | 任意(OpenAPI) | 必須(.proto) | 必須(.proto) |
| 型の自動生成 | OpenAPIがあれば可 | 可 | 可(TSの型が特に良い) |
| データ形式 | JSON | protobuf バイナリ | protobuf バイナリ / JSON |
| HTTP バージョン | 1.1 / 2 / 3 | HTTP/2 必須 | 1.1 / 2(gRPC利用時は2) |
| ブラウザから | そのまま使える | プロキシが必要 | そのまま使える |
| curl でのデバッグ | 簡単 | 専用ツールが必要 | 簡単(JSON時) |
| ストリーミング | SSE / WebSocket で別途 | 4種すべて | 4種(ブラウザはServerまで) |
| HTTPキャッシュ | 得意 | 使えない | GET対応メソッドのみ |
| エラー表現 | HTTPステータス+任意形式 | grpc-status(トレーラー) | HTTPステータス+定型JSON |
| エコシステム | 最大。誰でも知っている | 大きい。多言語対応が成熟 | 成長中(Go / TS / Swift / Kotlin など) |
| 学習コスト | 低い | 高め(HTTP/2・運用) | 中(protobuf と buf) |
選び方の目安
-
REST を選ぶ
外部に公開するAPI。利用者の技術がバラバラ。CDN キャッシュを最大限使いたい。チームが小さく、まず速く作りたい。 -
gRPC を選ぶ
多言語のマイクロサービス同士の通信。既に gRPC 基盤(サービスメッシュ等)がある。双方向ストリーミングが中心。 -
Connect を選ぶ
TypeScript のフロントとバックを型で繋ぎたい。gRPC の利点が欲しいがブラウザ対応やデバッグの手間は避けたい。既存 gRPC と共存させたい。
よくある全体構成
入口ごとに利用者に合った方式を選び、内側は同じ .proto 群で統一する。「全部を1つの方式に揃える」必要はありません。
ミドルへの視点:技術選定を ADR に残す
「なぜ Connect にしたのか」は、半年後に入ったメンバーには分かりません。ADR(Architecture Decision Record)という短い文書に、背景・検討した選択肢・決定・その結果生じるトレードオフを残しましょう。この比較表の観点をそのまま使えます。選ばなかった選択肢と、その理由を書くのがポイントです。
11 ミドルへのステップアップ
ここまでで、3つの方式の仕組みと実装は一通り理解できました。最後に、APIを扱うエンジニアがジュニアからミドルに上がるときに求められる視点を整理します。どれも方式に関係なく通用する考え方です。
ジュニアとミドルの違い
| 場面 | ジュニアの視点 | ミドルの視点 |
|---|---|---|
| API を作る | 仕様どおりに動く | 失敗したとき・変更するとき・負荷が10倍になったときにどうなるかまで考える |
| エラー | とりあえず 500 を返す | 呼び出し側が「リトライすべきか」「ユーザーに何を見せるか」を判断できるコードを返す |
| 変更 | フィールド名を直す | 既存クライアントへの影響を確認し、段階的に移行する |
| 障害 | ログを探し回る | リクエストIDとトレースで、どのサービスのどこで遅いかを特定する |
| 技術選定 | 流行っているものを使う | トレードオフを言語化し、ADR に残す |
| レビュー | コードの書き方を見る | 契約(スキーマ)・互換性・認可・タイムアウトを見る |
身につけるべき5つの柱
1. 契約思考
API はサーバーとクライアントの約束です。スキーマ(OpenAPI / .proto)を唯一の正しい情報源にし、実装はそこから導く。変更は「追加は自由、削除と変更は段階的に」。buf breaking や OpenAPI の差分チェックで機械に守らせる。
2. 障害を前提にした設計
ネットワークは必ず失敗します。タイムアウト(デッドライン)・リトライ(バックオフ付き)・冪等性の3点セットを常に意識しましょう。さらに、呼び出し先が落ちているときに呼び続けないサーキットブレーカーや、重要でない機能を切り離して本体を守るグレースフルデグラデーションも知っておくと設計の幅が広がります。
3. 観測可能性(オブザーバビリティ)
本番で何が起きているかを知る手段です。3本柱を押さえましょう。
- ログ:JSON 形式の構造化ログにし、リクエストID・ユーザーID・RPC名・所要時間・結果コードを必ず含める。
- メトリクス:RPC ごとのリクエスト数・エラー率・レイテンシ(p50 / p95 / p99)。「平均」ではなく「遅いほうの5%」を見るのがコツ。
- トレース:OpenTelemetry で、1つのリクエストが複数のサービスをどう渡り歩いたかを可視化する。インターセプターやミドルウェアで自動計測できる。
4. セキュリティ
入力検証(zod / protovalidate)、認証と認可(特にリソース所有者のチェック)、TLS、秘密情報をログやエラーメッセージに出さない、レート制限。OWASP API Security Top 10 は一度通読しておきましょう。
5. テスト
| 種類 | 対象 | API での例 |
|---|---|---|
| 単体テスト | service 層 | HTTP を使わず、業務ロジックだけを検証。一番数が多い。 |
| 結合テスト | ハンドラー〜DB | 実際にサーバーを起動し、クライアントから呼んでステータスやエラーコードを検証。 |
| 契約テスト | スキーマ |
buf breaking や OpenAPI の差分チェック。クライアントとの約束が守られているか。 |
Connect では、テスト用にネットワークを使わずサーバーの実装を直接呼べる createRouterTransport が用意されています。クライアントのテストでサーバーを簡単に差し替えられます。
import { createClient, createRouterTransport, ConnectError, Code } from "@connectrpc/connect";
import { TodoService } from "../src/gen/todo/v1/todo_pb.js";
import { routes } from "../src/connect-routes.js";
import { test } from "node:test";
import assert from "node:assert/strict";
test("存在しない ToDo は NotFound になる", async () => {
const client = createClient(TodoService, createRouterTransport(routes)); // メモリ内で完結
await assert.rejects(
client.getTodo({ id: "nope" }),
(e) => ConnectError.from(e).code === Code.NotFound,
);
});
演習課題
読んだだけでは身につきません。次の課題を順にやってみてください。後ろほどミドル寄りです。
-
REST にページングを足す
02章のGET /todosにカーソル方式のページングを実装する。limitの上限(例:100)も設ける。 -
冪等キーを実装する
POST /todosでIdempotency-Keyヘッダーを受け取り、同じキーなら前回の結果を返す。 -
.proto を拡張する
UpdateTodoとDeleteTodoを追加する。部分更新のためにoptionalやフィールドマスクをどう使うか調べて決める。buf lintを通すこと。 -
互換性を壊してみる
わざとTodoのdoneを削除し、buf breakingがどう検出するか確認する。次にreservedを使って正しく削除する。 -
REST と Connect を同居させる
02章の REST と 08章の Connect が、共通の service 層を呼ぶように書き換える。エラー変換関数を2つ(REST 用・Connect 用)作る。 -
観測可能性を入れる
インターセプターで構造化ログ(JSON)を出し、全ログにリクエストIDを含める。余力があれば OpenTelemetry でトレースを取る。 -
ADR を書く
「自分のチームの新規プロジェクトで REST と Connect のどちらを採用するか」を ADR 形式で1ページにまとめる。
自己チェックリスト
全部に自信を持ってチェックできたら、API 設計の議論でミドルとして発言できる状態です。
- HTTP リクエスト/レスポンスの生テキストを読んで、何が起きているか説明できる
- 安全・冪等の違いと、それがリトライ設計にどう関係するか説明できる
- 4xx と 5xx、gRPC の各コードを適切に使い分けられる
- protobuf のフィールド番号の役割と、互換性を守るルールを説明できる
- gRPC がブラウザで使えない理由と、Connect がそれをどう解決したか説明できる
- 全ての外部呼び出しにタイムアウトを設定する習慣がある
- 認可(リソース所有者チェック)をレビューで確認できる
- スキーマの変更が既存クライアントに与える影響を判断できる
- 業務ロジックを通信方式から分離した構成で実装できる
- REST / gRPC / Connect の選定理由をトレードオフ込みで説明できる
次に読むもの
- Connect 公式ドキュメント:Node / Web のガイドとプロトコル仕様。
- Protocol Buffers Language Guide (proto3):型と互換性の公式解説。
- gRPC Guides:デッドライン、リトライ、ヘルスチェックなど運用面のガイド。
- Google API Improvement Proposals (AIP):リソース指向の RPC 設計の実践的なルール集。ページングやフィールドマスクの設計に役立つ。
- OWASP API Security Top 10:API で起きやすい脆弱性の一覧。
- RFC 9457 Problem Details:REST のエラー形式の標準。
用語集
| 用語 | 意味 |
|---|---|
| API | プログラム同士がやりとりするための窓口と約束事。 |
| リソース | REST で操作の対象となる「モノ」。URL で指し示す。 |
| エンドポイント | API の個々の入口。GET /todos や TodoService/GetTodo。 |
| ステータスコード | HTTP レスポンスの結果を表す3桁の数字。 |
| 冪等性 | 何回実行しても結果が同じになる性質。リトライの安全性に関わる。 |
| シリアライズ | プログラム内のデータを JSON やバイナリなど送れる形に変換すること。 |
| HTTP/2 | 1本の接続で複数のやりとりを同時に行える(多重化)、バイナリ形式の HTTP。 |
| トレーラー | ボディの後ろに送られるヘッダー。gRPC は結果をここに入れる。 |
| RPC | Remote Procedure Call。遠くの関数を手元の関数のように呼ぶ考え方。 |
| Protocol Buffers | Google が作ったスキーマ言語兼バイナリ形式。.proto ファイルに書く。 |
| フィールド番号 | protobuf で各フィールドを識別する番号。通信ではこれが送られる。変更禁止。 |
| コード生成 | スキーマから各言語の型やクライアントを自動で作ること。 |
| ストリーミング | 1回の呼び出しで複数のメッセージを連続して送受信すること。 |
| デッドライン | 「この時刻までに返して」という締め切り。gRPC / Connect のタイムアウト。 |
| メタデータ | gRPC における HTTP ヘッダーのこと。認証情報などを載せる。 |
| インターセプター | 全ての RPC に共通する処理を挟み込む仕組み。ミドルウェアに相当。 |
| トランスポート | Connect で「どのプロトコルでどこへ送るか」を表す部品。差し替えるだけで方式が変わる。 |
| gRPC-Web | ブラウザ向けに gRPC を改変したプロトコル。 |
| Buf / buf CLI | .proto の lint・互換性チェック・コード生成を行うツール。 |
| BSR | Buf Schema Registry。.proto を公開・共有するレジストリ。 |
| OpenAPI | REST API の仕様を YAML / JSON で書く標準形式。 |
| CORS | ブラウザが別オリジンの API を呼ぶときの許可の仕組み。 |
| L4 / L7 LB | 接続単位で振り分けるか、リクエスト単位で振り分けるかの違い。gRPC には L7 が必要。 |
| ADR | Architecture Decision Record。技術的な決定とその理由を残す短い文書。 |
| OpenTelemetry | ログ・メトリクス・トレースを収集する標準仕様とツール群。 |
コード例は connect-es v2 / @grpc/grpc-js / Express 5 / zod を前提にしています。ライブラリのメジャーバージョンが変わると API が変わることがあるので、実装時は公式ドキュメントも併せて確認してください。
ここまで読んでいただきありがとうございました。