現代のウェブ開発において、Representational State Transfer (REST) API は、スケーラブルで保守可能、かつ理解しやすいウェブサービスを構築するための事実上の標準となっています。RESTful API のまさに中心には、根本的な概念であるリソースがあります。リソースとは何か、どのように識別されるか、そしてどのように操作するかを理解することは、REST API を効果的に設計および利用するために最も重要です。この記事では、REST API におけるリソースの性質を深く掘り下げ、その特徴、コレクションとの関係、およびそれらに対して実行される一般的な操作について探求します。
REST の背後にある核となる考え方は、API が一連のリソースを公開し、クライアントはそれらの固有の識別子にリクエストを送信することでこれらのリソースと対話するというものです。しかし、「リソース」とは正確には何を構成するのでしょうか? REST API のコンテキストでは、リソースは名前を付けることができるほぼすべてのものである可能性があります。それは、顧客、製品、注文のような具体的なエンティティかもしれません。また、サービス、トランザクション、計算のような抽象的な概念である可能性もあります。重要なのは、識別および操作できる関心のある項目であるということです。
インターネット自体を広大なリソースの集まりと考えてみてください。オンラインでアクセスするすべてのウェブページ、画像、動画、ドキュメントはリソースであり、それぞれに固有のアドレス (URL) があります。REST API もこの哲学を採用しています。ソーシャルメディアプラットフォーム上のユーザープロフィールであれ、オンラインライブラリの特定の書籍であれ、特定の都市の天気予報であれ、それぞれが API が利用可能にするリソースです。
最大限の生産性で開発チームが協力して作業できる統合されたオールインワンプラットフォームが必要ですか?
Apidogはあなたのすべての要求に応え、Postmanをはるかに手頃な価格で置き換えます!
リソースの識別:URIの役割
決定的に重要なのは、REST API のすべてのリソースには少なくとも1つの固有の識別子が必要であるということです。この識別子は通常、Uniform Resource Identifier (URI) です。ウェブ API で使用される最も一般的な形式の URI は、Uniform Resource Locator (URL) であり、これはリソースを識別するだけでなく、その場所を見つける手段も提供します。
例えば、ブログを管理するための API では、特定のブログ投稿は /posts/123
のような URI で識別されるかもしれません。ここで、/posts
は投稿のコレクションを表し、123
はそのコレクション内の特定の投稿の固有の識別子です。同様に、ユーザーリソースは /users/john.doe
で識別されるかもしれません。
これらの URI の設計は、API 設計の重要な側面です。うまく設計された URI は、直感的で予測可能であり、開発者が理解しやすく使いやすいものです。それらは、アクセスされているリソースの性質を示す明確な道しるべとして機能する必要があります。良い実践としては、リソースを表すために名詞を使用すること(例:/products
、/orders
)であり、動詞(例:/getProducts
、/createOrder
)は避けるべきです。HTTP メソッド(GET、POST、PUT、DELETE)は、URI によって識別されるリソースに対して実行されるアクションを指定するために使用されます。
リソース vs. 表現:重要な区別
リソースとその表現の違いを理解することは重要です。リソースは概念的なエンティティそのものです。実際の「もの」(顧客、製品、アイデア)です。一方、表現は、特定の時点でのそのリソースの状態のスナップショットであり、通常、JSON (JavaScript Object Notation) や XML (eXetensible Markup Language) のような特定のメディアタイプでフォーマットされています。
クライアントが API からリソースを要求するとき、リソース自体(サーバー上に存在する抽象的な概念)を受け取るわけではありません。代わりに、そのリソースの表現を受け取ります。例えば、/users/jane.doe
を要求すると、API は次のような JSON 表現を返すかもしれません:JSON
{
"id": "jane.doe",
"firstName": "Jane",
"lastName": "Doe",
"email": "jane.doe@example.com",
"dateJoined": "2023-01-15T10:00:00Z"
}
この JSON オブジェクトはジェーン・ドウ本人ではありません。システムによって保存されている彼女のデータの表現です。同じリソースは、潜在的に複数の表現を持つことができます。例えば、API は、クライアントがコンテンツネゴシエーション(Accept
のような HTTP ヘッダーを使用)を通じて要求した場合、同じユーザーの XML 表現も提供できるかもしれません。
リソースの状態は時間とともに変化する可能性があります。ジェーン・ドウがメールアドレスを更新した場合、基となるユーザーリソースは変更されます。その後の /users/jane.doe
への要求は、この更新された状態を反映した新しい表現を返します。これが REST の「State Transfer」の部分が関係するところです。クライアントは、これらの表現を通じてリソースの状態を取得および操作することでリソースと対話します。
コレクション:他のリソースを含むリソース
多くの場合、リソースはコレクションにまとめられます。コレクション自体もリソースです。例えば、ブログ API の /posts
は、個々の投稿リソースを含むコレクションリソースです。同様に、/users
はユーザーリソースのコレクションになります。
クライアントが /products
のようなコレクション URI に GET リクエストを送信すると、API は通常、そのコレクション内のメンバーリソースをリストする表現を返します。多くの場合、各メンバーの概要情報が含まれます。このリストは次のようなものになるかもしれません:JSON
[
{
"id": "prod_abc",
"name": "Laptop Pro 15",
"price": 1299.99,
"link": "/products/prod_abc"
},
{
"id": "prod_xyz",
"name": "Wireless Mouse Ergonomic",
"price": 39.99,
"link": "/products/prod_xyz"
},
// ... その他の製品
]
コレクション内の各項目には、個々のリソースへのリンク(または独自の URI)が含まれていることがよくあります。これにより、クライアントは特定の製品の完全な詳細にナビゲートして取得することができます。
コレクションとそのメンバーの URI 設計は、論理的なパターンに従います。通常:
/resources
はコレクションを参照します(例:/orders
)。/resources/{id}
はそのコレクション内の特定のメンバーを参照します(例:/orders/567
)。
この階層構造により、API は直感的になり、コレクションとその要素間の概念的な関係と整合します。
リソースとコレクションに対する操作
REST API におけるリソースおよびコレクションとの対話は、標準的な HTTP メソッドを通じて行われます。これらのメソッドは実行されるアクションを定義します。最も一般的なメソッドは次のとおりです。
GET: リソースまたはコレクションの表現を取得するために使用されます。
GET /posts
はすべての投稿のリスト(コレクション)を取得します。GET /posts/123
は ID 123 の特定の投稿を取得します。GET リクエストは安全である必要があります。つまり、サーバーに副作用があってはなりません。データ取得のためだけに使用されます。また、べき等である必要があります。つまり、複数の同一の GET リクエストは、単一のリクエストと同じ効果を持つ必要があります(リソースがその間に変更されていないと仮定して、同じ表現を返します)。
POST: 主にコレクション内に新しいリソースを作成するために使用されます。リクエストボディには、作成される新しいリソースの表現が含まれるのが一般的です。
- 新しいブログ投稿の詳細(例:タイトル、コンテンツ、作成者)を含むリクエストボディを持つ
POST /posts
は、サーバーにその新しい投稿を作成するように指示します。サーバーは通常、201 Created
ステータスで応答し、応答のLocation
ヘッダーに新しく作成されたリソースの URI を含めることがよくあります。POST リクエストは一般に安全ではありません(新しいリソースを作成するため)し、べき等でもありません(複数の同一の POST リクエストは通常、複数の新しいリソースを作成します)。POST は、他の HTTP メソッドにうまく収まらない非べき等な操作(プロセスをトリガーしたり、既存の識別可能なリソースの更新にすぎない結果ではない処理のためにデータを送信したりするなど)にも使用できます。
PUT: 既存のリソースを完全に更新するために使用されます。リクエストボディには、リソースの完全な新しい表現が含まれている必要があります。URI によって識別されるリソースが存在する場合、それは新しい表現に置き換えられます。存在しない場合、一部の API はそれを作成することを選択するかもしれません(ただし、この動作は異なる場合があります)。
- 投稿 123 の更新されたタイトルとコンテンツを含むリクエストボディを持つ
PUT /posts/123
は、既存の投稿 123 を新しいデータで置き換えます。PUT リクエストは安全ではありません(リソースを変更するため)が、べき等です。同じ PUT リクエストを複数回送信しても、リソースの状態は同じになるはずです。例えば、投稿のタイトルを「新しいタイトル」に複数回更新しても、タイトルは「新しいタイトル」のままです。
DELETE: リソースを削除するために使用されます。
DELETE /posts/123
は、ID 123 のブログ投稿を削除します。DELETE リクエストは安全ではありません(データを削除するため)が、べき等です。リソースを複数回削除しても、一度削除した場合と同じ結果になるはずです(リソースは存在しません)。同じ URI へのその後の DELETE リクエストは、404 Not Found
または204 No Content
を返す可能性があります。
PATCH: 既存のリソースを部分的に更新するために使用されます。リソースの表現全体をクライアントが送信する必要がある PUT とは異なり、PATCH は変更点のみを送信できます。例えば、ユーザーのメールアドレスのみを更新するには、PATCH リクエストには新しいメールアドレスを含めるだけで済みます。
{"email": "new.email@example.com"}
のようなリクエストボディを持つPATCH /users/jane.doe
は、ジェーンのメールアドレスのみを更新し、名前などの他のフィールドは変更しません。PATCH リクエストは安全ではありません。そのべき等性は議論の余地があり、パッチ操作の性質に依存します。例えば、「説明に '!' を追加する」という PATCH 操作はべき等ではありませんが、「説明を '新しい値' に設定する」という操作はべき等です。
シングルトンリソース
コレクションとそのメンバーは一般的ですが、リソースがスタンドアロンのエンティティである場合もあり、これは「シングルトン」リソースと呼ばれることがよくあります。良い例としては、特定のアプリケーションの構成やシステムの現在のステータスが挙げられます。
例えば、/application/configuration
は、アプリケーションの構成設定を表すシングルトンリソースの URI になり得ます。この URI への GET
リクエストは現在の構成を取得し、PUT
リクエストはそれを更新するために使用できます。このコンテキストには構成の「コレクション」はなく、その構成だけが存在します。
同様に、/system/status
はシステムの現在の運用ステータスを表すことができます。
リソースベース API 設計のベストプラクティス
リソース中心の API を設計するには、エンティティを識別し、それらを URI にマッピングするだけでは不十分です。いくつかのベストプラクティスが、堅牢で使いやすい API に貢献します。
- URI には名詞を使用する: 前述のように、リソース URI は名詞である必要があります(例:
/products
、/users/{userId}/orders
)。動詞は HTTP メソッドのために予約されるべきです。 - 一貫性のある URI 命名: URI には一貫性のある命名規則を使用してください。コレクションには複数形の名詞が一般的に好まれます(例:
/customers
ではなく/customer
)。長いパスセグメントの可読性を向上させるために、アンダースコア(_
)やキャメルケースではなく、ハイフン(-
)を使用してください(例:/product-categories
)。 - URI をシンプルかつ階層的に保つ: リソース間の関係を反映する URI を設計してください。例えば、
/users/{userId}/accounts/{accountId}
は、アカウントがユーザーに属していることを明確に示しています。ただし、URI が扱いにくくなるような、過度に深いネストは避けてください。 - ステートレス性: クライアントからサーバーへの各リクエストには、リクエストを理解1し処理するために必要なすべての情報が含まれている必要があります。サーバー2はリクエスト間でクライアントのコンテキストを保存すべきではありません。これは REST の核となる原則であり、スケーラビリティに貢献します。
- HTTP メソッドを正しく活用する: GET、POST、PUT、DELETE、PATCH をその定義されたセマンティクスに従って使用してください。データを変更するために GET を使用したり、GET が適切な場合にデータを取得するために POST を使用したりしないでください。
- HTTP ステータスコードを適切に使用する: リクエストの結果を示すために標準的な HTTP ステータスコードを返してください(例:
200 OK
、201 Created
、204 No Content
、400 Bad Request
、401 Unauthorized
、403 Forbidden
、3404 Not Found
、500 Internal Server Error
)。これにより、クライアントに明確なフィードバックが提供されます。 - コンテンツネゴシエーションをサポートする: クライアントが
Accept
ヘッダーを使用して希望する表現形式(例:JSON、XML)を指定できるようにし、Content-Type
ヘッダーを使用してリクエストボディの形式を示せるようにしてください。 - バージョニング: バージョニング戦略(例:
/v1/products
)を実装することで、API の進化に備えてください。これにより、既存のクライアントに影響を与えることなく破壊的な変更を導入できます。 - 意味のあるエラー表現を提供する: エラーが発生した場合、何が問題だったかを説明する有用なエラーメッセージを応答ボディ(通常は JSON または XML)で返してください。
- アプリケーション状態のエンジンとしてのハイパーメディア(HATEOAS): 常に完全に実装されるわけではありませんが、HATEOAS は REST の重要な原則です。これは、リソースの表現に、クライアントが関連するアクションやリソースを発見できるリンク(ハイパーメディアコントロール)を含める必要があることを意味します。例えば、注文の表現には、注文をキャンセルしたり、出荷ステータスを表示したり、含まれる製品を見たりするためのリンクが含まれる場合があります。これにより、API はより自己発見可能になります。
リソースの粒度
一般的な設計上の課題の1つは、リソースの適切な粒度を決定することです。住所は独立したリソースであるべきか、それともユーザーリソースの一部であるべきか? 注文項目は個別のリソースであるべきか、それとも注文リソース内に埋め込まれるべきか?
唯一の正解はありません。特定のユースケースと、クライアントが通常どのようにデータと対話するかに依存します。
- 独立したリソース: エンティティ(住所など)が独立して作成、取得、更新、または削除できる場合、または複数の他のリソース間で共有される場合、それを独自の URI を持つ独立したリソースとしてモデル化する(例:
/addresses/{addressId}
)ことはしばしば理にかなっています。その後、他のリソースからリンクすることができます(例:ユーザーリソースにはaddressId
フィールドまたは/addresses/{addressId}
へのリンクが含まれる場合があります)。 - 埋め込みリソース/サブリソース: エンティティが親リソースと密接に結合しており、独立したライフサイクルを持たない場合、親リソースの表現の一部として、または
/users/{userId}/address
のようなパスを通じてアクセス可能なサブリソースとしてモデル化する方が良いかもしれません。サブリソースが常に親のコンテキストでアクセスされる場合、これによりクライアントの対話が簡素化されます。
選択にはしばしばトレードオフが伴います:
- チャッティネス: 多くの細かい粒度のリソースは、クライアントが複数のソースから完全な情報を組み立てる必要がある場合に、より多くの HTTP リクエスト(チャッティネスの増加)を引き起こす可能性があります。
- データの重複/複雑さ: リソースを埋め込むと、ペイロードが大きくなり、埋め込まれた情報がスタンドアロンのリソースとしても利用可能な場合にデータの重複や複雑さが発生する可能性があります。
- キャッシュ可能性: 独立したリソースは、独立してキャッシュするのが容易なことがよくあります。
- 操作の原子性: 単一の粗い粒度のリソースへの更新は本質的にアトミックです。複数の細かい粒度のリソース更新にわたる原子性の管理は、より複雑になる可能性があります。
これらの要素を慎重に検討し、API がどのように使用されるかを深く理解することが、リソースの粒度に関する正しい決定を下すために不可欠です。
最大限の生産性で開発チームが協力して作業できる統合されたオールインワンプラットフォームが必要ですか?
Apidogはあなたのすべての要求に応え、Postmanをはるかに手頃な価格で置き換えます!
結論
リソースは、あらゆる RESTful API の基本的な構成要素です。それらは、API が公開し、クライアントが対話できる「もの」を表します。リソースに固有の URI を割り当て、リソースとその表現を区別し、リソースを論理的なコレクションに整理することで、開発者は直感的でスケーラブルであり、REST の原則に準拠した API を作成できます。
標準的な HTTP メソッドを使用してリソースを定義、識別、および操作する方法を理解することは、API 設計者と利用者双方にとって不可欠です。URI 設計のベストプラクティス、HTTP ステータスコードの適切な使用、およびリソースの粒度に対する思慮深いアプローチと組み合わせることで、適切に定義されたリソースモデルは、機能的であるだけでなく、使いやすい API につながります。デジタルランドスケープが進化し続けるにつれて、リソース指向アーキテクチャの原則は、効果的なウェブサービス通信の基礎であり続けるでしょう。