Upgrade to Pro — share decks privately, control downloads, hide ads and more …

REST API設計の実践 – ベストプラクティスとその落とし穴

REST API設計の実践 – ベストプラクティスとその落とし穴

Postman API Night Tokyo 2025 Spring
https://postman.connpass.com/event/348232/

#APINight

Avatar for 武田 憲太郎

武田 憲太郎

May 26, 2025
Tweet

More Decks by 武田 憲太郎

Other Decks in Programming

Transcript

  1. はじめに 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 1 REST APIの開発について、現場で感じた課題や落とし

    ⽳、それらを通じて得たベストプラクティスを実例を 交え紹介します。 理論と実践、設計と実装、APIとクライアント、その 間にある"溝"を埋めるのが、このトークのゴールです。
  2. こんな課題に覚えがありませんか? 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 2 • APIファースト開発では:

    • クライアントの要望でAPI設計変更が発⽣ • 設計時の考慮漏れによるネットワークN+1 • コードファースト開発では: • 似たAPIが乱⽴ • 仕様やテストの品質課題
  3. API開発の難しさの本質 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 3 REST APIにおける「原則」とは?

    • ルールの画⼀化による設計のアウトソース • 全操作を「リソース+動詞(HTTPメソッド)」で表現可能と仮定 • 「これはREST APIです」の⼀⾔で多くの前提を共有できる • あらゆる要件が「原則」に準拠できるとは限らない • 設計と要件の間の "溝" • APIとクライアントの間の "溝" • それを許容できない場合gRPCやGraphQLを選択 • それでもREST APIを選択する理由
  4. API開発の難しさの本質 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 4 API開発で維持すべき「品質」とは? •

    提供側(API)の品質を維持する • 利⽤側(クライアント)の品質を維持させる 持続可能なREST APIとは? • 当然の前提: 利⽤側のニーズを不⾜なく満たせている • 全ての操作をRESTとして抽象化することで: • 提供側のコストが最⼩限に抑えられている • 双⽅のコミュニケーションコストを最⼩化できている • 埋められない "溝" を破綻なく浅くできている • これを⽬指すテクニックを紹介するのがこのトークのゴール
  5. ⾃⼰紹介 2025/05/26 #APINight @KentarouTakeda / 武⽥ 憲太郎 Webアプリケーションエンジニア • サーバサイド:

    PHP - 2002年頃〜 • 型の扱いが⽐較的緩かった⾔語 • フロントエンド: TypeScript - 2013年頃〜 • 発表当初より厳密な型システムを持っていた⾔語 APIへの型付けや仕様と実装の乖離に、 課題感を持ち続けている。 REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 5
  6. 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 6 1. REST API設計の基礎

    2. ケーススタディ a. 参照APIとN+1問題 b. 状態管理を伴う更新操作 3. まとめ
  7. REST API設計の基礎 動詞で操作を指定 URIでリソースを識別 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳

    7 動詞 操作 GET 取得 POST 作成 PUT 更新 PATCH 部分更新 DELETE 削除 例: URI(名前) 例: リソース /users/yokawasa X: @yokawasa /event/348232/ Connpass: 本⽇のイベント /event/348232/participation Connpass: 参加者 /event/348232/join Connpass: イベントへ参加 /event/348232/inquiry Connpass: イベントへ問い合わせ • 数多くのプラクティスも最終的にはこの2つに収斂する • より網羅的なプラクティスは次の資料を参照: Web API設計ガイドライン | Future Enterprise Arch Guidelines https://future-architect.github.io/arch- guidelines/documents/forWebAPI/web_api_guidelines.html
  8. 機能をそのままAPI化する例: X 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 8 •

    全てを「リソース+動詞」として扱う • 🙅フォローする → 🙆フォローというリソースを作成する • 🙅フォロー解除する → 🙆フォローというリソースを削除する • リソースの所有関係を名前の階層で表現する • tweets: (アカウントを問わない)ポスト • users/{user}/tweets: (特定アカウントの)ポスト 操作 動詞 リソース ポストを投稿 POST tweets ポストを閲覧 GET tweets/{tweet} ポスト⼀覧 GET users/{user}/tweets リポスト POST users/{user}/retweets フォロー POST users/{user}/following フォロー解除 DELETE users/{user}/following/{target}
  9. 複雑な操作を抽象化する例: GitHub 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 9 例:

    プルリクエスト 〜 レビュー 〜 マージ 1. プルリクエストを作成 2. レビュー開始(⾮公開状態) 3. レビューコメント投稿(複数‧⾮公開状態) 4. レビュー終了(⼀⻫に公開) 5. マージ
  10. 複雑な操作を抽象化する例: GitHub 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 10 1.

    POST pulls 2. POST pulls/{pull}/reviews • レビューIDが払い出される(ここでは⾮公開) 3. POST pulls/{pull}/comments 4. POST pulls/{pull}/reviews/{review}/events • 払い出されたIDを指定しコミット(ここで公開) 5. POST pulls/{pull}/merge
  11. 複雑な操作を抽象化する例: GitHub 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 11 •

    レビュー: IDの払い出しによるACIDへの準拠 • I: 独⽴性: 確定するまで他のユーザーからは不可視 • A: 原⼦性: レビュー中のコメントも⼀⻫に確定される • マージ: 内部動作を適切に抽象化 • git上は git merge, git commit の複数操作 • GitHubには3種類の「マージ」がある • APIはシンプルに POST merge とし詳細を隠蔽 • 適切な設計にはユースケースの洗い出しが不可⽋
  12. REST API設計の勘所 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 12 •

    全ての機能は最終的に「ストレージの読み書き」に⾏き着く • データベースレコード or ファイル or 何か に対するCRUD操作 • 基本的には「リソース+動詞」として表現できるはず • ただし実装の詳細をそのままAPI化してはいけない 🙅プロフィールと購読を設定し規約に同意してサインアップ 1. POST users 2. POST users/{user}/profile 3. POST users/{user}/subscriptions 4. POST users/{user}/agreement 5. PATCH users/{user} • ユースケースに応じた詳細の隠蔽が設計者の腕の⾒せ所
  13. 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 13 1. REST API設計の基礎

    2. ケーススタディ a. 参照APIとN+1問題 b. 状態管理を伴う更新操作 3. まとめ
  14. 参照APIとN+1問題 GitHub Issue⼀覧画⾯ Typeによる絞り込み 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳

    14 • Issue Typeで表⽰をフィルタ可能 • この情報は⼀覧画⾯に表⽰されていない • APIレスポンスにも存在しない 「Issue⼀覧画⾯にTypeの表⽰を追加したい」
  15. 参照APIとN+1問題 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 16 • 「1件取得」のユースケースはおそらく詳細画⾯

    • 返却は「ユーザー詳細」スキーマ paths: /users/{user}: get: summary: ユーザー1件取得 parameters: # 省略 responses: '200': content: application/json: schema: $ref: '#/components/schemas/User' components: schemas: User: summary: ユーザー詳細 type: object required: - id - name - avatarUrl - email properties: # 省略
  16. 参照APIとN+1問題 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 17 • 複数件取得のユースケースはおそらく⼀覧画⾯

    • 表⽰内容はある程度簡略化されているはず • レスポンスサイズにも注意が必要 • 返却は「ユーザー概要」スキーマ paths: /users: get: summary: ユーザー全件取得 responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/UserSummary' components: schemas: UserSummary: summary: ユーザー概要 type: object required: - id - name properties: # 省略
  17. 参照APIとN+1問題 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 18 • 被参照の際は全情報は要らないはず

    • 個⼈の経験: 多くのケースで「概要」スキーマと共通化が可能 • 「概要」スキーマのバリエーションを必要に応じて増やしても良い paths: /posts: get: summary: 投稿全件取得 responses: '200': content: application/json: schema: type: array items: $ref: '#/components/schemas/Post' components: schemas: Post: summary: 投稿概要 type: object required: - id - subject - user properties: # 省略 user: $ref: '#/components/schemas/UserSummary' ここでも「概要」スキーマを使う
  18. スキーマの依存管理とN+1の未然防⽌ 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 19 • 機能追加例:

    • 要件: ⼀覧画⾯を含む全ての「ユーザー」表⽰にアイコン 画像を追加したい • 設計: UserSummaryスキーマへのavatarUrlの追加
  19. 補⾜: スキーマの共有を伴う実装(案) 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 20 •

    アプローチ • エンティティからス キーマへの変換を • スキーマ毎に • 単⼀責務で実装 • 多くのフレームワーク は標準で対応している • Laravel: JsonResource • Rails: Jbuilder • Spring Boot: Jackson /** * PostモデルをPostSummaryスキーマへ変換 * * @property-read Post $resource */ class PostSummary extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->resource->id, 'subject' => $this->resource->subject, 'comment' => $this->resource->comment, 'user' => new UserSummary( $this->resource->user ), ]; } } スキーマの依存関係と実装の依存関係を⼀致 スキーマと変換クラスは常に1:1
  20. 補⾜: スキーマの共有を伴う実装(案) 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 21 •

    スキーマと変換クラスの実装は常に1:1 • スキーマの依存関係と変換クラスの依存関係は常に⼀致
  21. 参考: 仕様と実装を確実に⼀致させる 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 22 Postman

    API Night Tokyo 2024 Spring スキーマ駆動開発による品質とスピードの両⽴ 私達は何故、スキーマを書くのか
  22. 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 23 1. REST API設計の基礎

    2. ケーススタディ a. 参照APIとN+1問題 b. 状態管理を伴う更新操作 3. まとめ
  23. 状態管理を伴う更新操作 架空の事例: ECサイトの注⽂処理 1. 参照: カートに⼊った商品⼀覧を表⽰ 2. 参照: 適⽤可能なクーポンの⼀覧を表⽰ カートの内容に応じて適⽤可能なクーポンは変わる

    3. 更新: クーポンを選択 4. 参照: 利⽤可能な決済⼿段の⼀覧を表⽰ 決済⾦額に応じて利⽤可能な決済⼿段が変わる 5. 更新: 決済⼿段を選択 6. 参照: 指定可能な配送⽅法の⼀覧を表⽰ 配達先に応じて指定可能な配送⽅法が変わる 7. 更新: 配送⽅法を選択 8. 参照: 指定可能な配達時間の⼀覧を表⽰ 配当⽅法に応じて指定可能な配達時間が変わる 9. 更新: 配達時間を選択 10. 完了: ここまでで注⽂が確定される ポイント: •完了まで複数の操作 (選択)が必要 •前の選択を完了しない と次の選択肢が確定し ない •UXのため画⾯をいくつ かに分割している 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 24
  24. 案1: POSTは最終の「完了」時のみ 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 25 概要

    • 注⽂途中の⼀時的な状態はブラウザで管理 • ⼊⼒状態に応じた各選択肢をGETするAPIを⽤意 • POSTは最終的な注⽂確定時のみ 特徴 • ✅ これで問題ないのであれば最もシンプルな設計 • クライアントがSPAであることが暗黙の前提になる • ❌ MPAでの実装は⾮常に難しい
  25. 案2: カートに⼀時状態を保存 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 26 •

    ✅ MPAに対応できる • ✅ ユーザーの途中離脱も想定できる • ❌ 複数タブから⽭盾した操作が⾏われたら? • ❌ テスト難度が上がる • ❌ ユーザーの誤解も招きやすい
  26. 案3: ⼀時状態を管理するサブリソース 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 28 操作

    動詞 リソース 1. 注⽂を開始 POST cart/checkouts 2. クーポンを⼀覧 GET cart/checkouts/{checkout}/coupons 3. クーポンを選択 PATCH cart/checkouts/{checkout} 4. 決済⼿段を⼀覧 GET cart/checkouts/{checkout}/payment-methods 5. 決済⼿段を選択 PATCH cart/checkouts/{checkout} 6. 配送⽅法を⼀覧 GET cart/checkouts/{checkout}/shipping-methods 7. 配送⽅法を選択 PATCH cart/checkouts/{checkout} 8. 配達時間を⼀覧 GET cart/checkouts/{checkout}/shipping-times 9. 配達時間を選択 PATCH cart/checkouts/{checkout} 10. 注⽂内容の確認 GET cart/checkouts/{checkout} 11. 注⽂を確定 POST cart/commit • セッション(⼀時状態)を開始 • セッションID(チェックアウトID)が払い出される • ⼀時状態のみ更新 • ⼀時状態を確定(注⽂確定)
  27. 案3: ⼀時状態を管理するサブリソース 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 29 ポイント:

    • 注⽂開始時に cart の中⾝は cart/checkouts にコピーされる • 複数ブラウザから操作されても⽭盾した状態が作られることはない • 注⽂途中にカート内の商品⾃体が変更されても問題ない (またはそれを検知できる) • 「クーポン」「決済⼿段」等も⼀時状態の内部に閉じている • coupons, payment-methods 等のリソースは cart/checkouts/{checkout} の配下に配置
  28. 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 30 1. REST API設計の基礎

    2. ケーススタディ a. 参照APIとN+1問題 b. 状態管理を伴う更新操作 3. まとめ
  29. まとめ 2025/05/26 #APINight REST API設計の実践 ‒ ベストプラクティスとその落とし⽳ 31 REST API設計とは:

    • 設計や統合のコストを最⼩化するためのルールや暗黙の合意事項 • 極度に単純化されたルール ‒ 現実の要件に合うとは限らない • 原則に従うコスト、原則により削減できるコストのトレードオフ 原則に従えない場合: • 多くの場合は「従える」 ‒ ⾔葉の⾔い換えや要件の変形 • それでも従えない場合: "溝" を埋めるテクニックはある程度の形式化が可能 重要なこと: ユースケースと向き合う • 単純なユースケース: 何も考えなくても良い • 複雑なユースケース: エンドユーザーのUXに⽴ち戻る • 「なぜこの設計なのか?」という問いに答えられるかが⼀つの⽬安