1 ポイント 投稿者 GN⁺ 2025-01-24 | 1件のコメント | WhatsAppで共有
  • API設計の出発点はRPCとRESTの違いであり、3つの方式はいずれもHTTPと結び付けられるが、呼び出しの作り方とクライアントの構成モデルが異なる
  • RESTはサーバーが提供したURLをたどるハイパーテキストモデルであり、クライアントがURL形式を組み立てるOpenAPI型のAPIとは区別される
  • OpenAPIはURLパステンプレートとHTTPメソッドで操作を定義するため、実用的で広く使われているが、RESTよりもHTTPにマッピングされたRPCモデルに近い
  • gRPCはIDL、コード生成、バイナリペイロード、HTTP/2接続管理によってRPC APIを直接定義し、HTTPの詳細を隠蔽する
  • gRPCは両側に特別なソフトウェアが必要で、プロキシ拡張・同時更新防止・部分更新に制約があるため、内部APIやCloud Endpointsのような変換レイヤーを使える場合に特に適している

HTTPベースAPIを見る3つのモデル

  • HTTPをAPIの転送層として使う代表的な方式はRESTOpenAPIgRPCに分かれる
  • 多くの公開APIや社内分散APIがHTTPを使う理由の1つは、組織が80番と443番ポートのHTTPトラフィックのセキュリティ問題に慣れているためである
  • 3つの方式はいずれもHTTPと結び付けられるが、何をアドレスとして扱うか、そしてクライアントが呼び出しをどう作るかが異なる

REST: URLを組み立てずにたどる方式

  • RESTではクライアントはサーバーが渡したURLをそのまま使い、URL形式をAPI仕様の一部として理解したり組み立てたりしない
  • ブラウザは現在のページ、ブックマーク、ユーザーが入力したURLをたどり、URLからHTTPリクエスト情報を抽出したり相対URLを絶対URLに変換したりする程度しか行わない
  • REST APIの核心は、リソース間参照を他のリソースのURLとして表現するハイパーテキスト/ハイパーメディアの利用にある
  • REST方式ではすべての識別子をURLとしてやり取りする
    • POST /accountsaccount_URL
    • POST /subscriptionsaccount_URLを渡す → subscription_URL
    • GET {account_URL} → アカウントデータツリーを返す
  • RESTの利点は、Web自体の安定性、一貫性、普遍性に近く、HTTP/RESTのエンティティ指向モデルがAPIをより単純で規則的にできる点にある

OpenAPI: HTTPに直接マッピングされたRPCに近い

  • OpenAPIはpathsの下にURLパステンプレートを定義し、パスとHTTPメソッドの組み合わせを**オペレーション(operation)**と呼ぶ
  • /pets/{petId}のようなパスは、クライアントが{petId}の値を知っていて、URLテンプレートに埋め込んでHTTPリクエストを作ることを要求する
  • この方式ではクライアントがURL形式を詳しく理解している必要があるため、RESTのハイパーテキストモデルとは逆である
  • OpenAPIが広く使われる理由は明確である
    • 従来のRPCモデルに似ており、プログラマーになじみやすい
    • RPCの概念をHTTPリクエストに合わせて直接マッピングできる
  • 公開APIでは、標準的なHTTP技術だけでほぼすべての言語と環境からアクセスできるという利点が大きい
  • 一方で、URLパス、HTTPメソッド、パラメータマッピングを設計する必要があるため、API提供者と利用者の双方が学ぶべき詳細が増える

gRPC: HTTP/2を隠蔽するRPC実装技術

  • gRPCはHTTP/2を転送層として使うが、API設計者やクライアント・サーバーコードにはHTTPの詳細を露出しない
  • gRPCクライアントの呼び出しフローは単純である
    • 呼び出すプロシージャを決める
    • 使用するパラメータ値を計算する
    • コード生成されたstubにパラメータを渡して呼び出す
  • gRPCはinterface description languageベースでリモートプロシージャを定義するため、OpenAPIのようにURLパス・パラメータ・HTTPメソッドのマッピングを併せて表現する必要がない
  • コード生成、フレームワーク、ライブラリにより、クライアントライブラリやサーバー実装を作りやすい場合がある
  • バイナリペイロードとHTTP/2接続管理を活用して性能上の利点があり、同じ技術をgRPCなしで直接使うこともできるが、より多くの技術習得が必要になる

OpenAPIの代わりにgRPCを検討する場合

  • OpenAPIでAPIを設計する際には、操作とパラメータをURLパスとHTTPメソッドの組み合わせで表現する必要があり、選択肢が多く難しくなりがちである
  • RPC型モデルを使い続けるなら、gRPCはHTTPへのカスタムマッピングを自分で設計する負担を減らしてくれる
  • gRPCとOpenAPIは基本となるAPIモデルは似ているが、HTTPの露出方法が異なる
    • OpenAPIはHTTP転送の詳細をクライアントに露出し、設計者がマッピングを制御する
    • gRPCはあらかじめ定義されたマッピングと生成コードによってHTTPの詳細を隠蔽する
  • OpenAPIの大きな利点は、クライアントが標準HTTPツールと技術をそのまま使える点であり、多くのAPI設計者にとってこの利点は追加の設計コストを正当化する

RPCにエンティティ指向モデルを組み合わせる

  • gRPCやOpenAPIを使う場合でも、RPCメソッドをエンティティ中心に制限すればRESTの一部の利点を得られる
  • 手続き定義から始めるのではなく、リソースタイプを先に定義し、その後で各タイプに標準的なエンティティ操作を対応付ける方式である
  • 基本操作はCreate、Retrieve、Update、Delete、Listで、一般にCRUD plus Listとみなせる
  • 必要に応じて追加操作を置くこともできるが、エンティティ指向の考え方と手続き指向の考え方が混ざると、一部の利点が弱まる可能性がある
  • 手続きをエンティティタイプごとに束ねる方式は、オブジェクト指向言語の中心的な考え方の1つでもある

gRPCの制約と注意点

  • gRPCはクライアントとサーバーの両方に特別なソフトウェアが必要で、生成コードを双方のビルドプロセスに統合しなければならない
  • JavaScriptやPythonのように、開発環境でビルドプロセスがほとんど存在しないこともある動的言語の利用者にとっては、この要求が負担になりうる
  • Google Cloud EndpointsはgRPC APIをHTTPとJSONからアクセス可能にしてクライアントの選択肢を回復するが、すべての利用者がこれを使える、または同等機能を構築できるわけではない
  • REST APIはメタデータなしでも全体をクロールするボットを作りやすいが、gRPCやOpenAPIのようなRPC型APIでは、エンティティタイプごとに異なるAPIとメタデータ、または専用ソフトウェアが必要になる
  • HTTP APIではApigee EdgeのようなAPI管理ツールやプロキシを通じて、セキュリティ機能、入力検証、データ形式マッピング、ヘッダー・本文修正などを追加することが多いが、gRPCではこうしたプロキシ拡張がはるかに難しい場合がある
  • gRPCは同時更新によるデータ損失を防ぐ標準メカニズムを定義していない
    • HTTPはそのためにEtagIf-Matchヘッダーを提供する
  • gRPCは部分更新メカニズムも定義していない
    • HTTPにはPATCHがあり、JSON向けの標準としてJSON merge patchJSON patchがある
    • JSON merge patchはより単純だが、配列更新のようなすべてのケースを扱えない
    • JSON patchはより多くのケースを扱えるが、利用はより複雑である

選択基準

  • RESTハイパーテキストモデルをすでに設計できる、または学ぶ意思があるなら、安定性・一貫性・普遍性を目指す良い選択肢になりうる
  • OpenAPIは標準HTTP技術だけでアクセス可能なAPIを作れるが、RPCの概念をHTTPにマッピングする設計上の選択肢が増えるため、設計・実装・学習が難しくなることがある
  • OpenAPIを検討するAPIであれば、gRPCも併せて検討する価値がある。両者の基本APIモデルは比較可能で、gRPCはHTTPマッピングを自分で作る必要を減らしてくれる
  • gRPCは次の条件で特に魅力的である
    • Cloud Endpointsのような製品により、クライアントがgRPC技術を必ずしも採用しなくてよい場合
    • サーバーとすべてのクライアントの技術選択を制御できる内部APIである場合
  • gRPCをOpenAPIやRESTの代わりに採用する際は、Apigee EdgeのようなAPI管理ツールベースのプロキシでAPIの振る舞いを拡張または補正する機会が大幅に制限されうる点を考慮する必要がある

1件のコメント

 
GN⁺ 2025-01-24
Hacker Newsの意見
  • 時間を巻き戻せるなら、gRPCをそもそも学べないようにしたいと思うほど
    最初はそのビジョンに惹かれたが、数年たってみると厄介ごとが多すぎた。内部を隠してくれるという話も冗談に近く、10回に1回失敗するリクエストの原因を突き止めようとしてデバッグログを大量に吐き出しつつ、名前も曖昧なタイムアウト/リトライ設定を10〜20個調整することになった
    Mavenプラグイン、謎の deadline exceeded、HTTP/2を嫌うロードバランサー、ファイアウォールのせいで結局標準APIを使わなければならない状況、貧弱なドキュメント、可観測性の面で使い物になるエラーメッセージを得ることまで、全部が時間を食った

    • gRPCの問題はプロトコルやprotobuf自体より、少なくともJava側のツールチェーンがひどいことにある
      生成されるコードの開発者体験が悪く、クライアントスタブは final の具象クラスなのでテストでモックしづらく、サーバー実装もインターフェースではなく具象クラスを継承しなければならない
      サーバーメソッドは非同期シグネチャなので @Transactional のようなAOPベースの動作を壊し、例外サポートもなく、不変値クラスは良いが全部ビルダーで作らなければならない
      結局、SOAでgRPCを使うなら、gRPCのノイズを隠してテスト可能できれいなコードを得るために多くの配管コードを書く必要があり、ThriftのRPCコンパイラにも似た問題があり、さらに別の問題まで抱えている
    • その問題はgRPC/protobufの仕様自体より、特定の実装の問題に近いように見える
      最新の.NETとC#でのgRPC体験はかなり良く、MicrosoftもWCFのような既存のRPC技術を終了してgRPCに注力した
    • 最も大きく使ったプロジェクトがJavaだったが、protoc が生成したバインディング出力を検証する作業は、直接シリアライズするよりも冗長でエラーを起こしやすかった
      ワイヤープロトコルは型安全ではなく、型タグはあるが複数のデータ型で同じタグを再利用している。zig-zag整数エンコーディングも遅い
      RPCライブラリとしてはひどく、実際に使った中でこれより悪かったのはFlatBuffersだけだった
    • Mavenに言及しているのでJavaを使っているようだが、この8年ほどGoでプログラミングしてきた立場からすると、gRPC体験はかなり違う
      この違いがJavaのせいなのか、gRPC技術そのもののせいなのか気になる
    • Go+DartスタックでgRPCを何年も使っているが、こうした問題に遭遇したことはない
      Java+gRPCに特化した問題なのか気になる
  • 長年APIを作ってきており、gRPCとHTTP/RESTの両方を使ったことがあり、OpenAPI仕様からGoクライアントとサーバーを生成するライブラリ https://github.com/oapi-codegen/oapi-codegen も公開している。
    この文章で OpenAPIとRESTを区別するやり方には同意しにくい。OpenAPIはHTTP APIの動作を文書化する方法であり、RESTful APIでも完全に任意のAPIでも表現できる。ツールが解釈できるようにAPIを記述するスキーマ言語という点で、gRPCプロトコルを指定するProtocol Bufferファイルと概念的に似ている。
    gRPCはprotoをやり取りするRPCメカニズムだが、Googleがprotobufを公開した際に内部RPC層であるStubbyは公開せず、gRPCはStubbyほど優れてはいない。それでも転送効率は高く、拡張も比較的容易だ。
    ただしgRPCは主流のHTTPライブラリほど堅牢なエコシステムではないため、ロギングや認証のようなミドルウェアを自前で実装しなければならないことが多く、異なる言語で実装されたサービス間のRPCでは特にそうだ。
    ただ、gRPCの本当の問題はprotoファイルにあると思う。すべてのクライアントがサーバーと互換性のある .proto ファイルでビルドされていなければならないため、発見可能なプロトコルではない。HTTP APIはOpenAPI記述がなくても curl や手書きのコードで呼び出せるので結合がより疎で、そのぶん作業やデバッグがしやすい。

    • このブログが「OpenAPI」と呼んでいるものと、きちんとした REST の間には区別がある。ただし実際に本物のREST APIを作っているところはほとんどなく、大半はOpenAPI的なアプローチを採っている。
      Roy Fieldingの2000年の博士論文で定義されたRESTでは、ルートURLに GET すると 200 OK のレスポンス内にリンク群があり、そのリンクをたどりながらAPIが提供するすべてのリソースを探索できる必要があった。階層構造は許容されるが、すべてがリンクツリーのどこかからアクセス可能でなければならず、これは発見可能性を与えるための意図だった。
      実際、この20年間働いてきた先ではほぼすべて POST resource_name/resource_id/sub_resource/sub_resource_id/mutation_type か、会社の冪等性処理の方針に応じて PUT resource_name/resource_id/sub_resource/sub_resource_id を使っており、クライアントはSwagger/OpenAPIのような構造知識をもとに魔法のようなURLを組み立てていた。
      そのため厳密な人たちは、FieldingのREST定義を実装していないという意味で、実務の方式を「REST」ではなく「RESTful」と呼ぶことがある。
    • OpenAPIとRESTを区別した部分は自分にも混乱を招いた。文中でRESTと呼んでいるものは HATEOAS により近く見える。
      内部・外部クライアントを持つかなり大きなAPIを管理する立場からすると、OpenAPI仕様からコードを生成する流れも理解しにくい。生成されたスタブを埋めたあとでAPI仕様を反復的に改善すると、ツールは新しいスタブをまた生成し、手動マージが必要になり、APIが大きくなるほど関連する変更を見つけにくくなる。
      そこで go/ast などを使ってコードからOpenAPI仕様を生成する怪物を作った。完璧ではないが、EchoとGinの両方で動く95%解で、新しいエンドポイントが必要ならリクエスト/レスポンス構造体と空のハンドラを作ってからドキュメントを生成し、フロントエンド開発者に渡すという形で素早く進められる。
      ほとんどの開発者はOpenAPIでAPIをどう表現するか悩む必要がなく、ドキュメントも常にコードと一致する。
    • 最近は発見のための gRPC reflection がある: https://grpc.io/docs/guides/reflection/
    • OpenAPI仕様を活用して、生成型とともに SQLっぽいクエリ構文 も作っている。こうするとどんなサードパーティAPIでも同じ感覚で扱える。
      Apipe.new(GitHun) |> from("search/repositories") |> eq(:language, "elixir") |> order_by(:updated) |> limit(1) |> execute()
      利用可能なgRPC関数やサードパーティAPIのRESTfulな癖を知らなくてもよく、組み込みドキュメント化と型アクセスも維持できる。
      https://github.com/cpursley/apipe
      JS/TSプロジェクトにSupabaseのように組み込めるよう、TypeScriptアダプタ層も検討中だ。
      const { data, error } = await apipe.from('search/repositories').eq('language', 'elixir').order_by('updated').limit(1)
      この呼び出しはElixirプロキシ経由で、非同期処理やレート制限のような重い仕事を担わせることができる。
    • gRPCが発見可能なプロトコルではないという話は完全には正しくない。
      ProtobufのJSONシリアライズをもとにOpenAPI記述を作ってSwaggerで提供できるし、gRPC自体にも組み込みの reflection と、それを使う grpcurl ユーティリティがある。
  • いくつかのFAANGで働いた経験から言うと、Thrift/gRPC は内部サービスのルーティングには本当に有用だと思う
    ただし、その複雑さのかなりの部分は、ライブラリ、サービスディスカバリ層、ルーティングなどを作るチームが面倒を見てくれている。RPCプロトコルを使えば、一般的なJSON/RESTサービスでは難しい規模と速度でこうしたことが可能になる
    REST APIで動詞がにじみ出てこないものを見たことがないし、バックエンドのサービスメッシュを作る、あるいは2つのローカルサービスをネットワークストリームでつなぐなら、私は常にgRPCを選ぶ
    しかし、顧客向けやWebに公開する用途ではgRPCは絶対に使わない。RPCは多くの決定を固定し、「一つのやり方」を強制するからこそ強力だ。逆に、複数の技術スタックにまたがる多様なクライアントがサービスを使う必要があるなら、RESTのほうがずっとよい

    • 公開APIではやらないが、非公開APIでは単に POST /api/doThingy にJSON本文を送る形で処理している
      最低限のHTTPクライアントさえあれば誰でも参加できる簡単なRPCで、すべてのOSとブラウザでうまく動く。URLパスに入れるか、クエリパラメータに入れるか、本文に入れるかで格闘する必要もない
      BufやConnectのように不便さを減らそうとするサーバー系を使えば、gRPCでもHTTP経由のJSONを喜んで受け付けてくれる
    • Webではないクライアント/サーバーアプリケーションはどうなのか気になる
      オンラインゲームやMMOのように、RESTよりはるかにリアルタイム性の高い通信が必要なケースを思い浮かべるが、今はソケット接続の上に何かを載せて使うのか、よく分からない
    • 内部サービスルーティングでThrift/gRPCが福音だというなら、何と比べて そうなのか気になる
      他に何を試したのかも知りたい
    • 「動詞がにじみ出る」とはどういう意味なのか気になる
  • 双方向ストリーミングをするのでなければ、gRPCはたいてい時間の無駄だと思う
    実行時の推移的依存関係地獄やツールチェーン地獄があるし、Google内部で各実装を管理しているチーム同士も、基本機能がどう動くべきかについて哲学的に合意できていないように見える
    自分の言語を使わないチーム、特にGo/Python/Java以外、あるいはそれですら古いバージョンを使っているチームにgRPC APIを公開したり、市販の既製品と統合したり、ブラウザに公開したりすると、結局どれも 中間層 が必要になる

    • 複数の会社やチームでgRPCを使ってきたが、各社のエンジニア規模はだいたい100〜500人程度で、依存関係やツールチェーンの問題には遭遇しなかった
      gRPCはおおむね順調だった
    • ブラウザに直接公開すべきではないと思う。それはgRPCの得意分野ではないし、JSONに変換するカスタム層を作ったほうがよい
      バックエンドサービス間通信にRESTを使うのも、性能要件があるならあまり意味がないし、たまに小さなデータを受け取る呼び出しでない限り、人間が読めるプロトコル/APIを使う理由は弱い
    • ブラウザにgRPC APIを公開しようとして、「JSONらしい」インターフェースを作っていないと問い詰められたことがある
      oneof のサブタイプのうち、現在フィールドのない型を返すと、自動変換の結果が { "id": "id", "sub_type_two": { } } のようになった
      機能的には動作するし、後でフィールドが追加されてもそのコードは動き続ける。だが、Webの世界で空のオブジェクトで応答型を表すのは奇妙で、protobufを書いているときにはこうした問題が見えにくいことがある
    • 双方向ストリーミングは、「大規模に」運用したい対象にはたいてい悪い考えだ
    • Protobufはストリーミングに向いていない
      想定できるほぼすべてのバイナリプロトコルと比べても、アンチストリーミング 寄りだ
  • 会社でgRPCを使った唯一の経験は、「性能が必要だ」と別のシニア開発者が押し切ったプロジェクトだった
    結局フロントエンドが消費できる必要があったのでJSON APIも作ったし、その開発者以外にgRPC経験者はいなかった。肝心のその開発者自身もgRPC Pythonクイックスタート以上には踏み込んでおらず、バグ修正も手伝わなかった
    プロジェクトは数多くの理由でひどいものになり、gRPCを正当化できるような規模にもまったく達しなかった
    それでも、個人的に少し触ったgRPC自体は気に入ったし、もっと多くの作業と検討が必要だという印象だった。これは単に、私がJSON APIのほうをずっと多くやってきたからかもしれない

    • それはgRPC自体というより、採用前に ブラウザと互換性がないこと すら知らなかった「シニア」開発者への批判に近いように見える
  • ConnectRPC https://connectrpc.com/ を楽しく使っている
    gRPCの問題のある部分をかなり改善してくれるし、SafariがWebTransportを採用すれば、ConnectRPCはより良いストリーミングを実現できるのではと期待している
    最初は https://buf.build はやりすぎだと思っていたが、サードパーティのprotoファイルを1つずつダウンロードせずにインポートできる機能が決定打だった
    deps:
    - buf.build/landeed/protopatch
    - buf.build/googleapis/googleapis
    自動SDK生成も大きい。以前は約9言語向けSDKの自動生成を褒めようと思っていたが、ここ1〜2日で更新されたらしく、今では16言語に加え、OpenAPIやほかの新機能まで見える
    私もgRPCストリーミングの偽りの約束に振り回されたし、この文書は自分の経験とぴったり一致していた: https://connectrpc.com/docs/go/streaming/

    • ReactNativeで動かすために、ConnectRPCストリーミング用の小さな WebSocketベースのラッパー を作った
      そのおかげでブラウザでも双方向ストリーミングが使える
    • それでもProtocol Buffersを使っていて、私がgRPCで感じる問題の多くはまさにそこから来ている
    • SafariのWebTransport対応について最近何か進展があったのか気になる
  • Googleが業界全体に心理戦でも仕掛けたかのように、内部サービス間通信で gRPC を使わせる流れを作ったように感じる
    gRPC の開発者体験は REST よりかなり悪い
    誰かにエンドポイントを呼ぶ簡単なコマンド 1 つすら渡せず、標準化されていない追加ツールが必要になる。しかも生成されるクライアント側コードは、どの言語でもめったに見ないほど醜い塊だ

    • バックエンドの観点では、gRPC の開発者体験が REST より悪いという意見には強く同意しない
      プロトコル変更 1 つで、どの下位コンシューマーを更新して再デプロイすべきかを静的に把握できるので、数週間かかる作業が 1 時間の変更で済むことがある
      受け取るメッセージと送信するメッセージが即座に検証されることも分かるし、後で復元するために安価に保存しておくこともできる
      proto によって非常に読みやすい API ドキュメントが得られ、コードやビジネスロジックに埋もれない。バージョニングと廃止の意味論も組み込まれており、map を除けばより豊かなデータ構造もサポートしている
      それに比べると、JSON はバックエンドでは肥大化して古臭く見える
    • gRPC は重要な点で標準的だ
      データ型と関数シグネチャを書いておけば、実際の関数のように呼び出せるものが手に入り、シリアライズ/デシリアライズの定型処理ではなくビジネスロジックに集中できる
      Thrift も同様に、全部を手で組み立てるやり方よりはるかに優れているし、GraphQL はさらに良いと思う
    • 複数の会社で無理やり使わされたが、99% は不要な技術的負債への投資だった
      Go でも再生成と共有 proto のバージョン管理を把握するのは面倒で、言語が 1 つ増えるたびにさらに悪化する
      それでも、すべてのスタートアップはマイクロサービス 100 個と gRPC が必要だと思っているようだ
  • 「API が REST API であるなら、クライアントは URL の形式を理解する必要はなく、その形式はクライアントに渡す API 仕様の一部ではない」という文は、Roy Fielding の REST 定義と通じている
    Fielding は、REST API は初期 URI と標準化されたメディアタイプの集合以外の事前知識なしに開始されるべきで、その後のすべてのアプリケーション状態遷移は、サーバーが提示した選択肢をクライアントが選ぶ形で行われるべきだと書いている
    https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypert...
    何度も語られてきた話ではあるが、本当に RESTful なシステムでは、クライアントに与える「API 仕様」は初期エントリポイントの URI/URL だけであるべきだという点はいまでも興味深い

    • 自己記述的な REST という考え方は、今では HATEOAS としてよりよく知られている
      個人的には肥大化していて、現実の問題を解決しないと思う
      https://en.m.wikipedia.org/wiki/HATEOAS
    • 初期エントリポイントだけを公開する REST API に対して、どうやって API クライアントを書くのか気になる
      特に、クライアントが操作可能なリソースやリクエスト/レスポンスモデルをどう発見するのかが分からない
    • 本当に RESTful なシステムでは、クライアントに与える API 仕様が初期エントリポイントの URI/URL だけであるべきだ、という点には完全には同意しない
      設定はそうかもしれないが、API 仕様は URL 以上のものを含むべきであり、そのシステムが使うメディアタイプを詳しく説明する必要がある。つまり、HTTP リクエスト/レスポンス本文の説明が大部分を占める
      実際、リンク先にも「REST API は、リソースを表現しアプリケーション状態を駆動するメディアタイプの定義に、説明のための努力のほぼすべてを費やすべきだ」と書かれている
      要するに、単に application/json を返すのではなく、特定の +json のようなものを返すことになり、そこに含まれるのは単なる一般的な JSON ではなく、アプリケーションが理解すべき業務データであることが多い
      大衆的な議論では初期 URL にばかり焦点が当たり、Fielding の言う「メディアタイプを説明せよ」という作業の大半が抜け落ちてしまう。だから「URL 1 つで十分だ」と聞かされた人が「残りの仕様はどこにあるのか?」と尋ねるのは当然だ
    • 両者は同じことに同意している。ここでいう「URL の形式」は文字どおり URL の形式であって、リソースの形式ではない
      私もまた REST を理解していないブロガーの話かと思って記事を開いたが、著者は少なくとも基本概念は理解しているようだ
      gRPC も好きで、商用プロジェクトではかなり魅力的だが、個人的あるいは理想主義的なプロジェクトなら REST のほうがよいと思う
    • 良いアイデアが流行したあと、人々がそのアイデアを誤解したまま広め続ける典型例だ
  • データセンター内部で gRPC を使うのは好きではない
    性能を理由に選ばれがちだが、gRPC は高性能と言えるほどではなく、公開クライアントの品質も、特に C++/Java の中核実装以外ではかなり低い。たとえば Node.js 実装がそうだ
    protobuf を API 仕様として使うこと自体には反対しないが、TCP 上のフレーミングプロトコルと組み合わせて使えるべきだと思う。ただ、その種の RPC には明確な支配的選択肢がない
    Web ベースの API では可読性のあるペイロードのほうを好むが、通常は JSON を使うことで型の厳密さが緩み、バックエンド言語間の相互運用性の問題が生じる。特に Node.js では JSON.parse がスキーママッピング実装のように使われがちだ
    これを正しくやるには、スキーマからエンコーダーとデコーダーを明示的に生成する必要があり、そうなると JS の文脈で JSON を使う利点はある程度薄れてしまう

    • 性能を心配して gRPC を検討するほどなら、同時に Node.js をスタックの近くに置いておくことをなぜ許容できるのか気になる
    • 同意するし、JS のスキーマ問題には Zod が大いに役立つ
      Microsoft の TypeSpec プロジェクトも追っている: typespec.io
    • proto の主な利点は、複数言語間での相互運用性
      単一言語の技術スタックなら重要性は下がる。そして Google の主要言語以外を使うなら、その分だけ体験は良くない可能性が高い
    • 2023 年には、データセンター RPC 向けの非 TCP ベースプロトコル Homa に関する発表があった: https://youtu.be/xQQT8YUvWg8?si=g3u5TogBe0_QpPpj
  • gRPCは、Googleの外にいるそれ以外の人々にとっては、不必要に敷居が高く感じられた
    gRPCのJSクライアントは無駄に重く、かなり不透明だ。アイデアは良いが、RESTの「シンプルさ」に慣れた人たちと比べると実装面が惜しい

    • フロントエンド/バックエンドの境界で、REST/JSON陣営とRPC/protobuf/gRPC陣営が衝突する
      RPCは、データモデルのカーディナリティや関係性をひとつの規範的なパターンに無理やり押し込める必要がないため、意味論的にはより保守しやすい。APIが急速に変化する世界では、美しいRESTfulエンティティを当てはめるのは難しく、大きなチームと変化する要件/オーナーシップのもとでは、サービス中心設計のほうが優れている
      フロントエンド側はバックエンドシステムを保守しない。理解しやすいAPIを望み、RESTで抽象化できるエンティティを求める。そうした設計の最終的な受益者だ
      RESTにかかる労力は、APIを販売しており、サードパーティ開発者が主要顧客である会社では意味がある
      バックエンド開発では、protobufとバイナリのワイヤエンコーディングのほうが簡単だ。APIを定義して静的型付けの方法でサービス間共有でき、メッセージのエンコード/デコード時間も減る。JSONは意味論的でも型付きでもなく、オーバーヘッドも大きい
      逆にフロントエンドは、テキストとJSONをネイティブに扱う。protobuf定義をダウンロードしたり、バイナリデータを二級市民のように扱ったりしたくないし、ツールともきれいに噛み合わない
      gRPCにはルーティング、リトライ、付加チャネル、ストリーミング、プロトコル廃止のセマンティクスがうまく組み込まれているが、フロントエンドにはほとんど現れない。すべてバックエンド利用者のためのものだ
      結局のところ、100% フロントエンド/バックエンドのツール格差であり、インターフェースとユーザビリティの不一致だ
    • RESTはHTMLに似ている
      基本的にソースを見ることができ、人間が読めて、検査しやすい
      gRPCは機械同士が効率的に会話するためのもので、コーディングでもリクエスト/レスポンスの検査でも、人が介在すると少し不便だ
      文脈と目標が違っていたため、このような使い勝手の差は理解できる
    • 公式のgRPC JavaScript実装はいまひとつだ
      私の見るところでは、buf.build側の実装が良い
      https://buf.build/blog/protobuf-es-the-protocol-buffers-type...
    • gRPCは良いアイデアだが、自分が抱えていない Google的な問題 に対する解決策で重くなってしまった
      契約を伴うバイナリRPCのようなプロトコルが必要に見えるので多くのものがそれを選んだのだろうが、GoLangから離れるほど徐々に悪くなる
    • gRPCが輝く用途はある。ストリーミング がそのひとつで、ひとつの「接続」の中でメッセージストリームを透過的に送れる
      単純なCRUDサービスならRESTでも十分だ