{"id":24202083,"url":"https://github.com/jkitajima/http_api_design","last_synced_at":"2026-02-01T22:32:30.285Z","repository":{"id":243928697,"uuid":"813808675","full_name":"jkitajima/http_api_design","owner":"jkitajima","description":"HTTP API Design Guidelines","archived":false,"fork":false,"pushed_at":"2025-03-24T04:48:07.000Z","size":66,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-23T18:04:28.942Z","etag":null,"topics":["api","api-design","http","specification"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc-by-4.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jkitajima.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-11T19:31:43.000Z","updated_at":"2025-09-13T12:29:20.000Z","dependencies_parsed_at":"2024-06-12T04:29:38.745Z","dependency_job_id":"add923f7-9656-417b-ac49-f8dbff0daa83","html_url":"https://github.com/jkitajima/http_api_design","commit_stats":null,"previous_names":["jkitajima/http-api-design","jkitajima/http_api_design"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jkitajima/http_api_design","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkitajima%2Fhttp_api_design","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkitajima%2Fhttp_api_design/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkitajima%2Fhttp_api_design/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkitajima%2Fhttp_api_design/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jkitajima","download_url":"https://codeload.github.com/jkitajima/http_api_design/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkitajima%2Fhttp_api_design/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28993253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T22:01:47.507Z","status":"ssl_error","status_checked_at":"2026-02-01T21:58:37.335Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-design","http","specification"],"created_at":"2025-01-13T21:17:55.678Z","updated_at":"2026-02-01T22:32:30.268Z","avatar_url":"https://github.com/jkitajima.png","language":null,"readme":"# HTTP API Design Guidelines\n\nThe purpose of this document is to establish a shared vocabulary and a pragmatic (yet robust) approach to design an HTTP API using [JSON](https://www.json.org) (JavaScript Object Notation) as a data-interchange format.\n\n\n## Table of Contents\n\n\u003cul\u003e\n  \u003cli\u003e\u003ca href=\"#core-design-principles\"\u003eCore Design Principles\u003c/a\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"#an-api-is-a-relationship-of-entities\"\u003eAn API is a Relationship of Entities\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#entities-have-shapes\"\u003eEntities have shapes\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#id-as-a-pair\"\u003eID as a Pair\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#entities-relationships-are-nested-1-sided-relation\"\u003eEntities relationships are nested\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#the-id-pair-is-a-location\"\u003eThe ID Pair is a Location\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#nested-relationship-objects-are-expandable\"\u003eNested relationship objects are Expandable\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#entities-have-grammar\"\u003eEntities have grammar\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#single-responsibility-and-concern-separation\"\u003eSingle Responsibility and Concern Separation\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#dealing-with-collections\"\u003eDealing with Collections\u003c/a\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"#collections-have-operations\"\u003eCollections have operations\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#pagination\"\u003ePagination\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#sorting\"\u003eSorting\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#filtering\"\u003eFiltering\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#collections-represents-mn-sided-relations\"\u003eCollections represents M/N-sided relations\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#request-and-response-cycle\"\u003eRequest and Response Cycle\u003c/a\u003e\n    \u003cul\u003e\n      \u003cli\u003e\u003ca href=\"#request-http-methods\"\u003eRequest HTTP methods\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#response-http-status-codes\"\u003eResponse HTTP status codes\u003c/a\u003e\u003c/li\u003e\n      \u003cli\u003e\u003ca href=\"#json-response-document\"\u003eJSON Response Document\u003c/a\u003e\u003c/li\u003e\n    \u003c/ul\u003e\n  \u003c/li\u003e\n  \u003cli\u003e\u003ca href=\"#references\"\u003eReferences\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\n## Core Design Principles\n\n\n### An API is a Relationship of Entities\n\nAn API (Application Programming Interface) is represented as a **relationship** of **entities**.\n\nThese entities cooperate together in order to perform actions, manage state and transfer representations of entity data.\n\n\n### Entities have shapes\n\nClients can interact with entities in two shapes:\n1. A single **object**: `/objects/{id}`\n2. A **collection** of objects: `/objects`\n\n\n### ID as a Pair\n\nAn object **uniqueness** is defined as a **pair** of values (entity + id) and not a single value (id).\n\nIn other words, this does **not** define an object:\n\n```json\n{\n  \"id\": 1,\n  \"name\": \"Max\"\n}\n\n```\n\nValid forms of defining an object:\n\n```json\n{\n  \"entity\": \"users\",\n  \"id\": 1,\n  \"name\": \"Max\"\n}\n\n```\n\n```json\n{\n  \"entity\": \"dogs\",\n  \"id\": 1,\n  \"name\": \"Max\"\n}\n\n```\n\n\n\n### Relationships\n\nThere are 3 types of relationship between entities:\n- One-to-One (`1-1`)\n- One-to-Many (`1-N`)\n- Many-to-Many (`M-N`)\n\nThis means that, in every type of relationship, an object has possibly:\n- A nested object representing a foreign key relationship (\"1-sided\" relation)\n- A location for representing the collection of objects (\"M/N-sided\" relation)\n\n\n### Entities relationships are nested (1-sided relation)\n\nSince the single value of an id does not define an object, entities relationships are nested.\n\nIn other words, this does not define a relationship:\n```json\n{\n  \"id\": 1,\n  \"name\": \"Max\"\n  \"owner_id\": 1\n}\n\n```\n\nValid form of defining a relationship:\n```json\n{\n  \"entity\": \"dogs\",\n  \"id\": 1,\n  \"name\": \"Max\"\n  \"owner\": {\n    \"entity\": \"users\",\n    \"id\": 1\n  }\n}\n\n```\n\n\n### The ID Pair is a Location\n\nNow that the uniqueness of object is a pair consisting of both entity and id values, these two values together defines a location inside the API.\n\n`GET /dogs/1`\n```json\n{\n  \"entity\": \"dogs\",\n  \"id\": 1,\n  \"name\": \"Max\"\n  \"owner\": {\n    \"entity\": \"users\",\n    \"id\": 1\n  }\n}\n\n```\n\n`GET /dogs/1/owner` redirects to `GET /users/1`\n```json\n{\n  \"entity\": \"users\",\n  \"id\": 1,\n  \"name\": \"Max\",\n  \"age\": 12,\n  \"citizen_id\": \"123.456.789-00\"\n  \"phone_number\": \"91234-5678\"\n}\n\n```\n\n\n### Nested relationship objects are Expandable\n\nSince relationships are represented as nested objects, they are **expandable**.\n\n`GET /dogs/1?expand=owner`\n```json\n{\n  \"entity\": \"dogs\",\n  \"id\": 1,\n  \"name\": \"Max\"\n  \"owner\": {\n    \"entity\": \"users\",\n    \"id\": 1,\n    \"name\": \"Max\",\n    \"age\": 12,\n    \"citizen_id\": \"123.456.789-00\"\n    \"phone_number\": \"91234-5678\"\n  }\n}\n\n```\n\n\u003e [!IMPORTANT]\n\u003e Relationship objects must either return the identification pair (entity + id) **or** the full object data.\n\u003e \n\u003e **Do not** return partial representation of data.\n\n\n### Entities have grammar\n\nEntity endpoints are separated by grammar semantics:\n- A `noun` endpoint is used for **transferring entity state representations** (`/objects/{id}`)\n- A `verb` endpoint is used for performing **actions** (`/objects/{id}/do`)\n\nAn example for a `user_files` entity with the following schema:\n\n\n```yaml\nid: uuid\nname: string\nextension: string\nurl: string\n\n```\n\n`POST /user_files` will create a new object by transfering a representation:\n\n\n```json\n{\n  \"name\": \"gopher\"\n  \"extension\": \".png\"\n  \"url\": \"https://go.dev/blog/gopher\"\n}\n\n```\n\nWhile, for a file upload using `multipart/form-data`, an action should be used: `POST /user_files/upload`. Then, after server-side logic was processed, a response object would be:\n\n\n```json\n{\n  \"entity\": \"user_files\",\n  \"id\": \"13728a84-e1dc-4de6-8f88-f0ba574907ad\",\n  \"name\": \"gopher\"\n  \"extension\": \".png\"\n  \"url\": \"https://storage.com/blobs/gopher.png\"\n}\n\n```\n\n\n### Single Responsibility and Concern Separation\n\nIdeally, requests have a single responsibility. This principle promotes the modularization of the API, making it composable.\n\nFor instance, imagine that the client-side wants to create a `cars` object with a photo. Instead of uploading the photo image and submitting other data in a single call, the flow would be:\n\n\n1. Create a `cars` object\n\n`POST /cars`\n```json\n{\n  \"model\": \"Mazda RX-7\",\n  \"year\": 1978\n}\n\n```\n\nPossible response:\n\n```json\n{\n  \"entity\": \"cars\",\n  \"id\": 1,\n  \"model\": \"Mazda RX-7\",\n  \"year\": 1978,\n  \"photo\": null\n}\n\n```\n\n2. Upload car image by performing an **action**:\n\n`POST /cars/1/upload_photo` (using `multipart/form-data`)\n\nThis way, the logic of uploading the car image is decoupled for its creation, making the API more composable and making server-side logic easier to maintain, debug and reason about.\n\n\u003e [!NOTE]\n\u003e If the flow above requires a single call for a specific client (making car metadata and photo upload atomic), a wrapper endpoint can be exposed through a gateway (reverse proxy). This pattern is know as [BFF (Backend for Frontend)](https://learn.microsoft.com/en-us/azure/architecture/patterns/backends-for-frontends).\n\n\n## Dealing with Collections\n\nA **collection** in simply an array of entity objects.\n\n\n### Collections have **operations**\n\nCollections support the following operations:\n- Pagination\n- Sorting\n- Filtering\n\n\n### Pagination\n\nClients can paginate through a collection using the `page_number` and `page_size` query params (if those values are not sent by the client, server defaults will be used).\n\n`GET /nations?page_number=1\u0026page_size=2`: first page containing two `nations` objects.\n\n```json\n[\n  {\n    \"entity\": \"nations\",\n    \"id\": 1,\n    \"name\": \"Brazil\",\n    \"continent\": \"America\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 2,\n    \"name\": \"Uruguay\",\n    \"continent\": \"America\"\n  }\n]\n```\n\n`GET /nations?page_number=3\u0026page_size=3`: third page containing three `nations` objects.\n\n```json\n[\n  {\n    \"entity\": \"nations\",\n    \"id\": 7,\n    \"name\": \"Japan\",\n    \"continent\": \"Asia\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 8,\n    \"name\": \"China\",\n    \"continent\": \"Asia\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 9,\n    \"name\": \"South Korea\",\n    \"continent\": \"Asia\"\n  }\n]\n```\n\n\n### Sorting\n\nSorting order can be either `ASCENDING` or `DESCENDING`.\n\nSort nations by `name` in `ASCENDING` order: `GET /nations?sort=name`\n\n```json\n[\n  {\n    \"entity\": \"nations\",\n    \"id\": 122,\n    \"name\": \"Afghanistan\",\n    \"continent\": \"Asia\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 83,\n    \"name\": \"Albania\",\n    \"continent\": \"Europe\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 57,\n    \"name\": \"Algeria\",\n    \"continent\": \"Africa\"\n  }\n]\n```\n\nSort nations by `name` in `DESCENDING` order: `GET /nations?sort=-name` (hyphen/minus signal in front of the field name)\n\n```json\n[\n  {\n    \"entity\": \"nations\",\n    \"id\": 160,\n    \"name\": \"Zimbabwe\",\n    \"continent\": \"Africa\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 45,\n    \"name\": \"Zambia\",\n    \"continent\": \"Africa\"\n  },\n  {\n    \"entity\": \"nations\",\n    \"id\": 37,\n    \"name\": \"Yugoslavia\",\n    \"continent\": \"Europe\"\n  }\n]\n```\n\n\n### Filtering\n\nClients can filter the collection by entity properties using the `filter` query param. The following table shows available filter operators.\n\nFilter operator          | Description           | Expression example\n--------------------     | --------------------- | -----------------------------------------------------\n**Comparison Operators** |                       |\neq                       | Equal                 | city eq \"Redmond\"\nne                       | Not equal             | city ne \"London\"\ngt                       | Greater than          | price gt 20\nge                       | Greater than or equal | price ge 10\nlt                       | Less than             | price lt 20\nle                       | Less than or equal    | price le 100\n**Logical Operators**    |                       |\nand                      | Logical and           | price le 200 and price gt 3.5\nor                       | Logical or            | price le 3.5 or price gt 200\nnot                      | Logical negation      | not price le 3.5\n**Grouping Operators**   |                       |\n( )                      | Precedence grouping   | (priority eq 1 or city eq \"Redmond\") and price gt 100\n\nJust insert the expression as a value for the `filter` query param:\n\n`GET /nations?filter=continent eq \"Europe\"`\n\n\n### Collections represents M/N-sided relations\n\nRecalling, there are 3 types of relationship between entities:\n- One-to-One (`1-1`)\n- One-to-Many (`1-N`)\n- Many-to-Many (`M-N`)\n\nAs we've seen, [expandable nested objects](#nested-relationship-objects-are-expandable) represents **1-sided** relations. For **M/N relations** (one-to-many and many-to-many), we use collections.\n\n**One-to-Many (`1-N`)**: an **owner** has a collection of **dogs**.\n\n`GET /users/1/dogs` redirects to `/dogs?filter=owner.id eq 1` (fetch `dogs` collection filtering by the owner)\n\n```json\n[\n  {\n    \"entity\": \"dogs\",\n    \"id\": 1,\n    \"name\": \"Max\",\n    \"owner\": {\n      \"entity\": \"users\"\n      \"id\": 1\n    }\n  },\n  {\n    \"entity\": \"dogs\",\n    \"id\": 2,\n    \"name\": \"Scott\",\n    \"owner\": {\n      \"entity\": \"users\",\n      \"id\": 1\n    }\n  }\n]\n```\n\n\n**Many-to-Many (`M-N`)**: relationship between `orders` and `products`. An order has a collection of products, and a product has a collection of orders.\n\n`GET /orders/{id}/products`: list products in that order.\n\n`GET /products/{id}/orders`: list the orders that a given product is present.\n\n\n## Request and Response Cycle\n\n### Request HTTP methods\n\nHTTP methods follows the specifications of [RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods). A summary is presented below.\n\nHTTP method              | Common usage\n--------------------     | ---------------------\nPOST                     | Create a new resource by transferring a representation; perform an unsafe action\nGET                      | Fetch an object or a collection of objects; perform a safe (read-only) action\nPATCH                    | Update an existing object by transferring a partial representation of entity data\nPUT                      | Used for upserts\nDELETE                   | Client requests the deletion of an object\n\n\n### Response HTTP status codes\n\nStatus codes are categorized in four classes:\n\nStatus code range        | Result\n--------------------     | ---------------------\n2xx                      | Success\n3xx                      | Redirection\n4xx                      | Client-side errors\n5xx                      | Server-side exceptions\n\nMost commonly used response status codes are:\n\nStatus code                               | Result\n---------------------------               | ---------------------\n`200 OK`                                  | Fetched an object/collection; updated (PATCH) an object; performed a safe (read-only) action\n`201 Created`                             | Request processing resulted in the creation of an object; performed an unsafe action\n`202 Accepted`                            | Used for asynchronous requests\n`204 No Content`                          | Request succeeded but returned no data\n`400 Bad Request`                         | Malformed request syntax or invalid request semantics\n`401 Unauthorized`                        | Lacking or invalid authentication credentials\n`403 Forbidden`                           | Server understood the request but refuses to fulfill it. Given credentials does not have enough access level to the specified resource\n`404 Not Found`                           | Server did not find a current representation for the target resource\n`500 Internal Server Error`               | Server encountered an unexpected condition that prevented it from fulfilling the request\n\n\n### JSON Response Document\n\nUnless returning a `204 No Content`, requests will return a JSON document. This document have defined fields, making the results predictable and stardandized.\n\nThe document have possibly 4 root-level fields:\n- `meta`: optional object\n- `data`: only present if `errors` is absent. Either an object or an array of objects\n- `pagination`: only present if `data` is an array\n- `errors`: only present if `data` is absent\n\nIf the four root-level fields above are absent (meaning a successful request returning no data), it is a `204 No Content` status code.\n\n\n#### Meta Object\n\nThis object represents request metadata. Contains two defaults fields (`status` and `message`) and any other API-specific fields.\n\nExample (default fields; mostly for debugging)\n\n```json\n{\n  \"meta\": {\n    \"status\": 404,\n    \"message\": \"Could not find any user with provided ID.\"\n  }\n}\n```\n\nMore concrete examples\n\n```json\n{\n  \"meta\": {\n    \"status\": 201,\n    \"message\": \"Created\",\n    \"request_token\": \"32606149-b8a2-42b0-b507-92d7f7c22465\",\n    \"remaining_quota\": \"Your API request limit is current at 69% of your daily usage quota.\"\n  }\n}\n```\n\n\n#### Data Object/Array\n\nPrimary result of your request. The resulting data after requested was processed.\n\n`GET /nations/8`\n\n```json\n{\n  \"data\": {\n    \"entity\": \"nations\",\n    \"id\": 8,\n    \"name\": \"China\",\n    \"continent\": \"Asia\"\n  }\n}\n```\n\n\n#### Pagination Object\n\nIf `data` is an **array**, `pagination` is present. This object consists of four fields:\n- `total_pages`: Total number of pages to represent the collection of objects using the current page size\n- `current_page`: Current page number\n- `page_size`: Page size controls how many objects each page will return\n- `objects_count`: Count of the number of objects existing in this collection\n\n`GET /nations?page_number=3\u0026page_size=3`\n\n```json\n{\n  \"data\": [\n    {\n      \"entity\": \"nations\",\n      \"id\": 7,\n      \"name\": \"Japan\",\n      \"continent\": \"Asia\"\n    },\n    {\n      \"entity\": \"nations\",\n      \"id\": 8,\n      \"name\": \"China\",\n      \"continent\": \"Asia\"\n    },\n    {\n      \"entity\": \"nations\",\n      \"id\": 9,\n      \"name\": \"South Korea\",\n      \"continent\": \"Asia\"\n    }\n  ],\n  \"pagination\": {\n    \"total_pages\": 4,\n    \"current_page\": 3,\n    \"page_size\": 3,\n    \"objects_count\": 12\n  }\n}\n```\n\n\n#### Errors array\n\nArray of error objects detailing **client-side** errors. An **error object** consists of three fields:\n- `code`: application-specific error code\n- `title`: a short, human-readable title of the error caused by the client\n- `detail` (optional): error message providing details about the error in the current request context\n\n```json\n{\n  \"meta\": {\n    \"status\": 400,\n    \"message\": \"Failed to upload user profile image.\"\n  },\n  \"errors\": [\n    {\n      \"code\": 20202,\n      \"title\": \"Invalid media type\",\n      \"detail\": \"Tried to upload image of content-type 'image/heic' while only 'image/png' is supported.\"\n    },\n    {\n      \"code\": 15370,\n      \"title\": \"File too large\",\n      \"detail\": \"File upload limit is 5 MiB. Tried to upload a 17 MiB file.\"\n    }\n  ]\n}\n```\n\n## References\n\nJSON API\n- https://jsonapi.org\n- https://jsonapi.org/format/\n- https://jsonapi.org/format/#document-structure (Response document structure)\n- https://jsonapi.org/recommendations/\n- https://jsonapi.org/examples/\n\nMicrosoft REST API Guidelines\n- https://github.com/microsoft/api-guidelines\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#polymorphic-types (Polymorphic types)\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md?plain=1#L483 (\"kind\" field)\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/Guidelines.md#filter-operators (filtering collections)\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/ConsiderationsForServiceDesign.md\n- https://github.com/microsoft/api-guidelines/blob/vNext/azure/README.md\n\nHeroku Platform API (interagent)\n- https://geemus.gitbooks.io/http-api-design/content/en/\n- https://geemus.gitbooks.io/http-api-design/content/en/foundations/separate-concerns.html (Separate Concerns)\n- https://geemus.gitbooks.io/http-api-design/content/en/responses/nest-foreign-key-relations.html (Nest foreign key relations)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkitajima%2Fhttp_api_design","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjkitajima%2Fhttp_api_design","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkitajima%2Fhttp_api_design/lists"}