JSON:API仕様の完全入門

Rebecca Kovács

Rebecca Kovács

19 5月 2025

JSON:API仕様の完全入門

REST(Representational State Transfer)は、Webサービス構築のための基礎的なアーキテクチャスタイルを提供します。しかし、リクエストとレスポンスのフォーマットに関する多くの側面は未定義のままです。この曖昧さにより、一貫性の欠如、開発オーバーヘッドの増加、API利用者の学習曲線の急勾配化を招く可能性があります。ここで登場するのがJSON:APIです。これは、JSONでAPIを構築するための標準化された規約ベースのアプローチを提供する仕様です。

この包括的なガイドでは、JSON:API仕様を深く掘り下げ、そのコアコンセプト、構造、強力な機能を探求します。リソースの取得、作成、更新、削除、リレーションシップの管理、エラー処理、データ転送の最適化に関するそのメカニズムを分析し、堅牢で効率的なAPIを設計・利用するための知識を提供します。

💡
美しいAPIドキュメントを生成する優れたAPIテストツールをお探しですか?

開発チームが最大限の生産性で連携するための、統合されたオールインワンプラットフォームをお探しですか?

Apidogはあなたのすべての要求に応え、Postmanをより手頃な価格で置き換えます
ボタン

なぜJSON:APIなのか?解説:

技術的な複雑さに入る前に、JSON:APIが解決しようとしている問題を理解することが重要です。共通の規約がない場合、API開発者はしばしばかなりの時間をかけて議論します。

JSON:APIは、リクエストとレスポンスに対して明確で一貫したフォーマットを定義することで、これらに対応します。この標準化にはいくつかの重要な利点があります。

コアコンセプト:JSON:APIドキュメントの構成要素

JSON:APIの中心にあるのは、リソースという概念です。リソースは、「記事」、「ユーザー」、「製品」などの特定のタイプの個別のレコードです。リクエストまたはレスポンスのいずれであっても、すべてのJSON:APIドキュメントは特定の構造に従います。

ドキュメント構造:トップレベルメンバー

JSON:APIドキュメントは、以下のトップレベルメンバーの少なくとも1つを含まなければならないJSONオブジェクトです。

さらに、ドキュメントは以下のトップレベルメンバーを含むこともあります。

リソースオブジェクト:データを表現する

リソースオブジェクトはJSON:APIの要であり、以下を含まなければなりません。

リソースオブジェクトは以下を含むこともあります。

リソースオブジェクトの例:JSON

{
  "type": "articles",
  "id": "1",
  "attributes": {
    "title": "JSON:API Unveiled",
    "body": "A deep dive into the specification...",
    "created_at": "2025-05-15T10:00:00Z",
    "updated_at": "2025-05-16T14:30:00Z"
  },
  "relationships": {
    "author": {
      "links": {
        "self": "/articles/1/relationships/author",
        "related": "/articles/1/author"
      },
      "data": { "type": "users", "id": "42" }
    },
    "comments": {
      "links": {
        "self": "/articles/1/relationships/comments",
        "related": "/articles/1/comments"
      },
      "data": [
        { "type": "comments", "id": "5" },
        { "type": "comments", "id": "12" }
      ]
    }
  },
  "links": {
    "self": "/articles/1"
  }
}

リソース識別子オブジェクト

リソース識別子オブジェクトは、typeidのみを含む、リソースの最小限の表現です。リレーションシップオブジェクト内で使用され、完全なリソースオブジェクトを埋め込むことなく他のリソースにリンクします。

リソース識別子オブジェクトの例:JSON

{ "type": "users", "id": "42" }

リンクオブジェクト

リンクオブジェクトは、APIをナビゲートするためのURLを提供します。一般的なリンクメンバーは以下を含みます。

リンクは以下のように表現できます。

リンクオブジェクトの例(リレーションシップ内):JSON

"links": {
  "self": "http://example.com/articles/1/relationships/author",
  "related": "http://example.com/articles/1/author"
}

メタオブジェクト

メタオブジェクトを使用すると、非標準のメタ情報を含めることができます。これは任意のキーと値のペアにすることができます。たとえば、metaオブジェクトには、著作権情報やデータに関連するタイムスタンプを含めることができます。

メタオブジェクトの例:JSON

"meta": {
  "copyright": "Copyright 2025 Example Corp.",
  "authors": ["John Doe"]
}

コンテンツネゴシエーション:適切な言語を話す

JSON:APIは独自のメディアタイプを定義しています:application/vnd.api+json

サーバーは、標準的なコンテンツネゴシエーションを通じて、application/vnd.api+jsonと並行して他のメディアタイプをサポートできます。

データの取得:リソースとコレクションの取得

JSON:APIは、クライアントが必要なデータを正確に取得するための堅牢なメカニズムを提供します。

個別のリソースの取得

単一のリソースを取得するには、クライアントはそのリソースを表すエンドポイントにGETリクエストを送信します。

リクエスト:

GET /articles/1

Accept: application/vnd.api+json

成功レスポンス(200 OK):JSON

{
  "links": {
    "self": "/articles/1"
  },
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON:API Rocks!"
    }
    // ... 他の属性とリレーションシップ
  }
}

リソースが存在しない場合、サーバーは404 Not Foundを返すべきです。to-one関連リソースリンクが取得され、リレーションシップが空の場合、プライマリデータはnullになります。

リソースコレクションの取得

リソースのコレクションを取得するには、クライアントはそのコレクションを表すエンドポイントにGETリクエストを送信します。

リクエスト:

GET /articles

Accept: application/vnd.api+json

成功レスポンス(200 OK):JSON

{
  "links": {
    "self": "/articles",
    "next": "/articles?page[offset]=10",
    "last": "/articles?page[offset]=50"
  },
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": { "title": "Article 1" }
      // ...
    },
    {
      "type": "articles",
      "id": "2",
      "attributes": { "title": "Article 2" }
      // ...
    }
    // ... 他の記事
  ]
}

コレクションが空の場合、dataメンバーは空の配列[]になります。

リレーションシップ:リソースの接続

リレーションシップは、ほとんどのデータモデルの基本的な部分です。JSON:APIは、それらを定義し操作するための明確な方法を提供します。

リレーションシップの表現

リレーションシップは、リソースのrelationshipsオブジェクト内で定義されます。relationshipsオブジェクト内の各エントリは、個別のリレーションシップを表します(例:「author」、「comments」)。

リレーションシップオブジェクトは、以下の少なくとも1つを含まなければなりません。

「author」(to-one)および「comments」(to-many)リレーションシップの例:JSON

"relationships": {
  "author": {
    "links": {
      "self": "/articles/1/relationships/author",
      "related": "/articles/1/author"
    },
    "data": { "type": "users", "id": "42" }
  },
  "comments": {
    "links": {
      "self": "/articles/1/relationships/comments",
      "related": "/articles/1/comments"
    },
    "data": [
      { "type": "comments", "id": "5" },
      { "type": "comments", "id": "12" }
    ]
  }
}

リレーションシップの取得

クライアントは、提供されたリンクを使用して、リレーションシップ自体または関連リソースに関する情報を取得できます。

リレーションシップリンケージの取得(selfリンク):

GET /articles/1/relationships/comments

Accept: application/vnd.api+json

これは、記事「1」に関連するコメントのリソース識別子オブジェクトのコレクションを返します。

関連リソースの取得(relatedリンク):

GET /articles/1/comments

Accept: application/vnd.api+json

これは、記事「1」に関連する完全なコメントリソースオブジェクトのコレクションを返します。

データ取得の最適化

JSON:APIは、データ取得方法を最適化し、帯域幅を最小限に抑え、クライアント側のパフォーマンスを向上させるためのいくつかの機能を提供します。

複合ドキュメント:includeによるHTTPリクエストの削減

関連リソースを取得するためにサーバーへの複数回の往復を避けるために、JSON:APIではクライアントがincludeクエリパラメータを使用して関連リソースをプライマリレスポンスに含めるように要求できます。サーバーはこれらのリソースをトップレベルのincluded配列にサイドロードします。

記事を取得し、その著者とコメントを含めるリクエスト:

GET /articles/1?include=author,comments

Accept: application/vnd.api+json

レスポンス(200 OK):JSON

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": { "title": "..." },
    "relationships": {
      "author": {
        "data": { "type": "users", "id": "42" }
      },
      "comments": {
        "data": [
          { "type": "comments", "id": "5" },
          { "type": "comments", "id": "12" }
        ]
      }
    }
  },
  "included": [
    {
      "type": "users",
      "id": "42",
      "attributes": { "name": "John Doe" }
    },
    {
      "type": "comments",
      "id": "5",
      "attributes": { "body": "Great article!" }
    },
    {
      "type": "comments",
      "id": "12",
      "attributes": { "body": "Very informative." }
    }
  ]
}

スパースフィールドセット:必要なフィールドのみの取得

クライアントは、fields[TYPE]クエリパラメータを使用して、特定のタイプのリソースに対して特定のフィールド(属性とリレーションシップ)のみを返すように要求できます。これによりペイロードサイズが削減されます。

記事を取得するが、そのタイトルと著者リレーションシップのみを取得するリクエスト:

GET /articles?fields[articles]=title,author

Accept: application/vnd.api+json

レスポンス(200 OK):JSON

{
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "Article 1"
      },
      "relationships": {
        "author": {
          "data": { "type": "users", "id": "42" }
        }
      }
    }
    // ... タイトルと著者のみを持つ他の記事
  ]
}

ソート

クライアントは、sortクエリパラメータを使用してプライマリデータをソートするように要求できます。

作成日(降順)、次にタイトル(昇順)でソートされた記事を取得するリクエスト:

GET /articles?sort=-created_at,title

Accept: application/vnd.api+json

ページネーション

JSON:APIは様々なページネーション戦略をサポートしています。仕様では、ページネーションリンク(firstprevnextlast)がトップレベルのlinksオブジェクトにどのように表示されるべきかを定義しています。実際のページネーション戦略(例:ページベース、オフセットベース、カーソルベース)は、page[number]page[size]page[offset]page[limit]、またはpage[cursor]のようなクエリパラメータを使用してサーバーによって決定されます。

ページベースのページネーションリンクの例:JSON

"links": {
  "self": "/articles?page[number]=2&page[size]=10",
  "first": "/articles?page[number]=1&page[size]=10",
  "prev": "/articles?page[number]=1&page[size]=10",
  "next": "/articles?page[number]=3&page[size]=10",
  "last": "/articles?page[number]=5&page[size]=10"
}

クライアントは、独自のページネーションURLを構築するのではなく、提供されたこれらのリンクを使用すべきです。

フィルタリング

仕様では、データのフィルタリングのためにfilterクエリパラメータを予約しています。しかし、特定のフィルタリング戦略を義務付けてはいません。サーバーは、filter[attribute]=valueのような戦略や、より複雑な式ベースのフィルタリングなど、任意の戦略を実装できます。

例(JSON:APIによって推奨されていますが、義務付けられていません):

GET /comments?filter[post]=1 (ID 1の投稿のコメントを取得)

GET /comments?filter[post]=1,2&filter[author]=12 (投稿1または2のコメントを、著者12によって取得)

クライアントは、APIのドキュメントを参照して、その特定のフィルタリング機能について理解すべきです。

データの変更:リソースの作成、更新、削除

JSON:APIは、データ操作のための明確なプロトコルを定義しています。

リソースの作成

リソースを作成するには、クライアントはリソースのコレクションを表すURLにPOSTリクエストを送信します。リクエストボディは、type、およびオプションでattributesrelationshipsを含む単一のリソースオブジェクトを含まなければなりません。クライアントは、新しいリソースのidを提供してはなりません(クライアント生成IDがサポートされ有効になっている場合を除く)。

リクエスト:

POST /articles

Accept: application/vnd.api+json

Content-Type: application/vnd.api+jsonJSON

{
  "data": {
    "type": "articles",
    "attributes": {
      "title": "New Article Title",
      "body": "Content of the new article."
    },
    "relationships": {
      "author": {
        "data": { "type": "users", "id": "42" }
      }
    }
  }
}

成功レスポンス:

既に存在するクライアント生成IDを持つリソースを作成しようとした場合、かつサーバーがPOSTによる更新をサポートしていない場合、409 Conflictを返さなければなりません。

リソースの更新

リソースはPATCH HTTPメソッドを使用して更新されます。リクエストには、更新するリソースのidを含まなければなりません。リクエストボディには、typeid、および更新するattributesおよび/またはrelationshipsを含むリソースオブジェクトが含まれます。

記事のタイトルとそのリレーションシップの1つを更新するリクエスト:

PATCH /articles/1

Accept: application/vnd.api+json

Content-Type: application/vnd.api+jsonJSON

{
  "data": {
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "Updated Article Title"
    },
    "relationships": {
      "tags": {
        "data": [
          { "type": "tags", "id": "3" },
          { "type": "tags", "id": "4" }
        ]
      }
    }
  }
}

更新の重要なポイント:

成功レスポンス:

更新するリソースが存在しない場合、サーバーは404 Not Foundを返さなければなりません。

リレーションシップの直接更新

JSON:APIは、プライマリリソースの属性に影響を与えることなくリレーションシップを管理する特定の方法を提供します。

{
  "data": { "type": "users", "id": "24" } // 新しい著者を割り当てるか、nullにしてクリアする
}
{
  "data": [
    { "type": "comments", "id": "101" },
    { "type": "comments", "id": "102" }
  ]
}
{
  "data": [
    { "type": "comments", "id": "103" }
  ]
}
{
  "data": [
    { "type": "comments", "id": "5" }
  ]
}

リレーションシップの更新が成功した場合、通常は200 OKまたは204 No Contentを返します。

リソースの削除

リソースを削除するには、クライアントはそのリソースのエンドポイントにDELETEリクエストを送信します。

リクエスト:

DELETE /articles/1

Accept: application/vnd.api+json

成功レスポンス:

リソースが存在しない場合、サーバーは404 Not Foundを返すべきです。仕様は、削除時に関連リソースやリレーションシップがどのように処理されるべきか(例:カスケード削除)を規定していません。これは実装の詳細です。

エラー処理

エラーが発生した場合、サーバーは適切なHTTPステータスコード(クライアントエラーには4xx、サーバーエラーには5xx)を使用しなければなりません。レスポンスボディにはJSON:APIエラードキュメントを含めるべきです。

エラードキュメントにはトップレベルのerrorsメンバーが含まれます。これはエラーオブジェクトの配列です。各エラーオブジェクトは以下を含むことができます。

エラーレスポンスの例(422 Unprocessable Entity):JSON

{
  "errors": [
    {
      "status": "422",
      "source": { "pointer": "/data/attributes/email" },
      "title": "Invalid Attribute",
      "detail": "The email address is not valid."
    },
    {
      "status": "422",
      "source": { "pointer": "/data/relationships/author" },
      "title": "Invalid Relationship Value",
      "detail": "Author with ID '999' does not exist."
    }
  ]
}

サーバー側の考慮事項とベストプラクティス

コア仕様は包括的ですが、JSON:APIはURL設計やメンバー命名などの側面についても推奨事項を提供しています。

URL設計

メンバー命名

仕様の拡張:拡張機能とプロファイル

JSON:APIは、進化するニーズと特定のユースケースに対応するために拡張可能に設計されています。

拡張機能

拡張機能は、基本仕様でカバーされていない新しい機能を追加できます。例としては、「Atomic Operations」拡張機能があり、単一のアトミックなリクエストで複数の操作(作成、更新、削除)を実行できます。クライアントとサーバーの両方が拡張機能を理解している場合にのみ使用できます。サーバーがサポートされていない拡張機能を含むリクエストを受け取った場合、適切なエラーで応答しなければなりません。

プロファイル

プロファイルは、特定のユースケース(例:タイムスタンプの特定の処理方法や、共通のmeta属性セット)のために、基本仕様の上に一連の規約を定義します。拡張機能とは異なり、プロファイルは一方の当事者によって理解されていなくても安全に無視できます。これらは、コア仕様への変更を必要とせず、普遍的なサポートを義務付けることなく、一般的なパターンの相互運用性を促進することを目的としています。

サーバーは、サポートされている拡張機能とプロファイルをトップレベルのjsonapiオブジェクトで宣伝できます。これにより、クライアントはこれらの機能を検出し、それに応じてリクエストを調整できます。

JSON:APIの未来

JSON:APIは、コミュニティの意見と新たなAPI設計の課題に対処する必要性に牽引され、進化を続けています。設定よりも規約、効率、開発者エクスペリエンスへの焦点は、最新のAPI構築における主要な標準としての地位を確立しています。JSON:APIを採用することで、開発チームは曖昧さを大幅に削減し、相互運用性を向上させ、APIの開発と利用のペースを加速させることができます。

この詳細な探求は、JSON:API仕様の大部分を網羅しています。これらの原則を理解し実装することで、開発者は機能的であるだけでなく、クリーンで一貫性があり、作業しやすいAPIを作成でき、最終的にはより生産的で協力的なAPIエコシステムを育むことができます。

ApidogでAPIデザイン中心のアプローチを取る

APIの開発と利用をよりシンプルなことにする方法を発見できる