Restful API設計でのリソース設計とHTTPステータスを考えてみる

とあるSAP案件でサーバーサイド側を一任され、リソース設計から担当していました。

TaNA
こーゆーのは最初の設計がイケてないと、使い辛い&保守し辛いシステムになるので責任重大。とは言え1からリソース設計した事が無かったので、とりあえず先人が遺してくれたQiitaの素晴らしい記事を読み漁ることに。

更にこの有名な本を再度読み返していました。

その案件ではなんやかんや2週間くらいで設計し、1ヶ月くらいでの開発でした。

本当に良いリソース設計だったか不安ですが、その時の事をメモ書きです。

RESTとは何か!?

RESTとはWebのアーキテクチャスタイル。

TaNA
本書ではRESTを、クライアント/サーバから派生したアーキテクチャステイルであり、素のクライアント/サーバアーキテクチャスタイルに幾つかの制約を加えたものがRESTになる紹介されています。

POINTRESTの意味はリソース(Web上の情報)の状態を表現するもの。

RESTではクライアント/サーバに他の制約を課す事で構成されています。

1.クライアント/サーバ

2.ステートレスサーバ

3.キャッシュ

4.統一インタフェース

5.階層化システム

6.コードオンデマンド

では実際にRestfulなAPIを実装するためにリソースの設計はどうすればよいのか!?

リソース設計とは!?

リソース設計とはクライアント・サーバ間のインタフェース設計(Web APIの外部設計)。

TaNA
オブジェクト指向やリレーショナルデータベースに比べ、リソース設計には一般的な設計手法が無いとのことですが、本書ではリソース指向アーキテクチャの設計アプローチが紹介されています。

リソース指向アーキテクチャのアプローチは以下のとおりです。

■ リソース指向アーキテクチャの手順

1.Webサービスで提供するデータを特定

2.データをリソースに分ける

3.リソースにURIで名前をつける

4.提供するリソース表現を設計

5.リンクとフォームでリソース同士を結びつける

6.イベントの標準的なコースを検討

7.エラーについて検討

1と2はデータベース設計等でやるイメージですが、まぁ考えるべきは3、4、7でしょうか。

良いURI設計とは!?

ちなみに良いURI設計については、このように紹介されています↓

POINT良いURI・綺麗なURI(クールURI)は変わらない。

3と4について「良いURI」設計のための勘所が記載されています。

■ URIの設計指針

・言語依存の拡張子はNG

・実装依存のパスはNG

・言語のメソッド名はNG

・セッションIDを含めない

・リソースを表現する名詞

ここまでは教科書に載っていた内容。

Qiitaでは実戦的に分かりやすく説明されている記事を発見。

翻訳: WebAPI 設計のベストプラクティス

設計・開発時はこちらを参考に試行錯誤していました。

ステータスコード

主に利用するメソッドはGET、POST、PUT、DELETEの4つ。

ネット上の色んなサイトを参考にこんな感じで定義していました。

■ GET

・200 – 成功時

■ POST

・201 – 成功時

・400 – バリデーションエラー

■ PUT

・200 – 成功時

・400 – バリデーションエラー

・409 – 対象リソース無し

■ DELETE

・200 – 成功時

・409 – 対象リソース無し

■ 共通的なもの

・401 – 認証エラー

・503 – 予期せぬエラー

DELETEの成功時は204にする事もある模様。

リソースをHTTPメソッドで操作

RESTのキーは「論理的に分割されたリソース」をHTTPメソッドで操作する事。

TaNA
実際のフレームワーク開発では、名詞(使う側が客観的に見て分かる名前[複数形])で名付けられたコントローラクラスに対し、HTTPメソッド(GET、POST、PUT、DELETE等)でリソース操作を行うよう設計を考えます。

RESTの原則に従い、HTTPメソッドを使ってCRUD操作は以下のように定義。

・GET:リソースの取得

・POST:リソースの新規作成

・PUT:リソースの更新

・DELETE:リソースの削除

例えばUsersテーブルに対して取得・登録・更新・削除を行う場合は以下のようなイメージ。

・GET /users – ユーザーのリストを取得

・GET /users/1 – 指定ユーザーの情報を取得

・POST /users – 新しいユーザーを作成

・PUT /users/1 – ユーザー #1を更新

・DELETE /users/1 – ユーザー #1 を削除

KISSの原則によれば、リソースは複数形が良いそうです。

関連データを取得する場合は!?

関連データの取得ですが、あるリソースに付随してのみ存在する場合は簡単。

・GET /users/1/skills – ユーザー #1に紐づく資格リスト取得

・GET /users/1/skills/2 – ユーザー #1に紐づく資格 #2の情報取得

・POST /users/1/skills – ユーザー #1に紐づく資格情報の登録

・PUT /users/1/skills/2 – ユーザー #1に紐づく資格 #2の更新

・DELETE /users/1/skills/2 – ユーザー #1に紐づく資格 #2の削除

関連データがリソースから独立して存在する場合は!?

レスポンス値に関連データのキー情報を含めるよう設計する。

複数テーブル結合の場合は!?

この名詞選びが意外に難しいです。

単純に1つのテーブルに対してなら、上の設計で問題無いのですが…

TaNA
実際の業務では、複数テーブルを結合して必要なデータを取得しますが、その場合の名詞はどうするべきか!? 複数テーブルを結合し、元のテーブルが意味するリソースと別の意味を持つリソースを取得する場合は!? 自分の場合、使う側が分かりやすいよう複数テーブルが表現するデータの概念を名詞にしていました。

例えばテーブルに本、利用者、貸出リストの場合は以下の通り。

・GET lendinguser/{userId} – 本を借りているユーザー取得

実際、複数テーブルを結合する場合でも、その複数のテーブルが扱うリソースが単一テーブル名から、それ程逸脱していなければ、テーブル名をそのまま使っている場合も(^▽^;)

CRUDに合致しない場合は!?

自分の場合、一括削除とかがそうでした。

最初は「DELETE /articles」のように設計を考えました。

TaNA
だたこれでは「/articlesという記事の集合リソース自体を削除」するように解釈される可能性があるので、Developers.IOさんのブログではあまり推奨されない模様。

そのブログでは以下を推奨されていました。

POINTPOST + 末尾に動詞

・POST /articles/delete

ちょっとリソース設計に名詞ではなく、動詞が入っているのは気になりますが…

Swagger Editorで定義

API定義はSwaggerで管理していました。

開発効率を上げる!Swaggerの記法まとめ

Node.jsを活用すれば、簡単なスタブは作れるそうです。