Consolidated Design Rules and Standards for Great REST API - Part 2
HTTP Methods Rules
In the previous part, we covered the generic rules that apply to all HTTP methods. In this part, we will address each HTTP method separately.
GET HTTP Method Rules
Rule 100: Use GET for retrieval only, and it should always be safe and idempotent. The resource state after sending the GET request must be precisely the same as the resource state before sending the GET request.
Rule 101: Don't use get for unsafe operations; in case of network failures, consumers should be able to resubmit the request safely.
Rule 102: Use Get to perform safe non-CRUD actions (e.g., validate, calculate, search, compare, etc.) and use query parameters to pass inputs. For such activities, use a verb as a sub-source. /users/compare
Rule 103: Don't use GET on the same URI to perform multiple operations. The below examples are not allowed (the below technique is called Tunneling, which is not recommended at all)
#Request 1
GET /users
Content-Type: application/json
{op="validate", users=[]}
#Request 2
GET /users
Content-Type: application/json
{op="calc", users=[]}
Rule 104: If the consumer didn't provide or provided invalid or expired credentials (In the Authorization header), return the HTTP Status code (401 Unauthorized), and it is recommended to return the WWW-Authenticate header to tell the client what kind of authentication is expected
Rule 105: If the consumer provided un-authorized credentials, return HTTP status code (403 Forbidden)
Rule 106: if the requested resource (collection or object) doesn't exist and the server doesn't know if the resource was there before or not, return the HTTP Status code (404 Not Found)
Rule 107: if the requested resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version.
Rule 108: if the requested object resource doesn't exist and the server knows it was there before, return (405 Gone). This is applicable in this case a logical delete mechanism is implemented at the level of the object.
Rule 109: if the requested resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 110: If the request has a payload but doesn't include a Content-Type header. Return status code (400 Bad Request).
Rule 111: If the request has an unsupported media type defined in the Content-Type header, return status code (415 Unsupported Media Type)
Rule 112: If the request has a body, but it doesn't match the media type defined in the Content-Type header, return status code (400 Bad Request)
Rule 113: If the request has a body and it matches the media type defined in the Content-Type header, but the message structure or values are invalid, return status code (400 Bad Request)
Rule 114: In response, return the payload using the same format used in the request payload (e.g., request Content-Type). If the request has no payload, use the Accept header to allow the consumer to negotiate for his preferred data format (e.g., JSON, XML, etc.). If no Accept header is provided in the request, use the default data format (e.g., JSON).
Rule 115: if the client sends Accept header with a media type that can't be supported by the server, return (406 Not Acceptable)
Rule 116: In case of abnormal failures, return HTTP status code 5XX
Rule 117: In case of errors or failure, return an error report in the response body describing the issue.
Rule 118: Content-Type header must be used in the response and must match the response body payload (for example: don't return application/json while the response payload is XML-based)
Rule 119: Query parameters can be used for filtration, sorting and pagination:
- Filtration example; get all active users: /users?active=true
- Pagination example; get page 5 where the page size is 10: /users?pageId=5&pageSize=10
Rule 120: Return status code (200 OK) for a successful response to the GET request
Rule 121: Don't use the Status code (200 OK) and there is an error in the response body
Rule 122: Avoid range requests using the Range header for implementing pagination (e.g., Range: 1–100)
Rule 123: Return the Last-Modified header to inform the consumer when was the last update of the resource
Rule 124: Return the ETag header to inform the consumer about the latest version of the resource. ETag can be generated using MD5 hash value
Rule 125: Allow conditional GET requests by supporting If-Modified-Since and If-None-Match (if provided by the client):
- Client propagate If-Modified-Since with previously retrieved Last-Modified Or Client propagate If-None-Match with previously retrieved ETag
- The server validates the value, if the condition is true, that means the resource has been updated, and the server must return a fresh representation for the resource with HTTP Status Code (200 OK)
- If the condition is false, that means the resource is not updated, and the server must return the HTTP Status code (304 Not Modified)
Rule 126: If response integrity is important, make sure to sign the response payload (see Rule#81)
Rule 127: Allow caching for successful GET response using all of the below headers in the response:
- For Legacy HTTP1.0 use Date:<start-date> and Expires:<cache-end-date> headers
- For HTTP1.1 and above use Cache-Control: max-age=<Seconds>, must-revalidate
Rule 128: For critical data that are not supposed to be cached, disallow caching using all of the below headers in the response:
- For Legacy HTTP1.0 use Pragma: no-cache and Expires:0 headers
- For HTTP1.1 and above, use Cache-Control: no-cache
OPTIONS HTTP Method Rules
Rule 129: Use OPTIONS to retrieve the resource's allowed methods. It is good to support it, although it is not popularly used.
Rule 130: Use OPTIONS for retrieval only, and it should always be safe and idempotent. The resource state after sending the OPTIONS request must be exactly the same as the resource state before sending the OPTIONS request.
Rule 131: Return Allow header with a list of supported HTTP methods in response to the OPTIONS request. if we send the following request, we would get the following HTTP header in the response: OPTIONS /users
Allow: GET, HEAD, PUT, POST, DELETE
Rule 132: When a resource supports the PATCH method, add an Accept-Patch header listing the media types supported for PATCH requests:
#Request
OPTIONS /customers/1
#Response
HTTP/1.1 204 No Content
Allow: POST, GET, PATCH
Accept-Patch: application/json
Rule 133: Add a Link header in the response containing a human-readable document that describes the resource
Rule 134: PATCH request and response should not contain a body
Rule 135: Return HTTP Status Code (204 No Content) in case of success
Rule 136: if the requested resource (collection or object) doesn't exist and the server doesn't know if the resource was there before or not, return the HTTP Status code (404 Not Found)
Rule 137: if the requested resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version.
Rule 138: if the requested object resource doesn't exist and the server knows it was there before, return (405 Gone). This is applicable in this case a logical delete mechanism is implemented at the level of the object.
Rule 139: if the requested resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 140: In case of abnormal failures, return HTTP status code 5XX
Rule 141: In case of errors or failure, return an error report in the response body describing the issue.
POST HTTP Method Rules
Rule 142: Use POST to create new parent objects. In the below example, a new user will be created:
POST /users
Rule 143: Use POST to execute unsafe non-CRUD operations (such as lock, unlock, merge, copy, duplicate, process, etc.). Such activities are sometimes called tasks, and the resources handling tasks are called controllers or processors. For such tasks, use a verb as a sub-source.. For example:
- To merge the addresses of user Id 121, use: /users/121/addresses/merge
- To lock a user with id 121, use: POST /users/121/lock
Rule 144: Don't use POST on the same URI to perform multiple operations. The below examples are not allowed (the below technique is called Tunneling, which is not recommended at all)
#Request 1
POST /users
Content-Type: application/json
{op="async", users=[]}
#Request 2
POST /users
Content-Type: application/json
{op="lock", users=[]}
Rule 145: If the consumer didn't provide or provided invalid or expired credentials (In the Authorization header), return the HTTP Status code (401 Unauthorized), and it is recommended to return the WWW-Authenticate header to tell the client what kind of authentication is expected
Rule 146: If the consumer provided un-authorized credentials, return HTTP status code (403 Forbidden)
Rule 147: if the requested collection resource doesn't exist and the server doesn't know if the resource was there before or not, return the HTTP Status code (404 Not Found)
Rule 148: if the requested collection resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version or not.
Rule 149: if the requested collection resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 150: If the request has a payload but doesn't include a Content-Type header. Return status code (400 Bad Request).
Rule 151: If the request has an unsupported media type defined in the Content-Type header, return status code (415 Unsupported Media Type)
Rule 152: If the request has a body, but it doesn't match the media type defined in the Content-Type header, return status code (400 Bad Request)
Rule 153: If the request has a body and it matches the media type defined in the Content-Type header, but the message structure or values are invalid, return status code (400 Bad Request)
Rule 154: if the client sends Accept header with a media type that can't be supported by the server, return (406 Not Acceptable)
Rule 155: In case of abnormal failures, return HTTP status code 5XX
Rule 156: Return an error report in the response body describing the issue in case of errors or failure.
Rule 157: Content-Type header must be used in the response and must match the response body payload (for example: don't return application/json while the response payload is XML-based)
Rule 158: After creating the resource, return the HTTP response code (201 Created) and use one of the following headers:
- Use the Location header pointing to the newly created object if the response payload is empty
- Use the Content-Location header pointing to the newly created object if the response payload contains the representation of the newly created resource. This will inform the client that the content can also be retrieved by calling this URI.
Rule 159: Avoid returning a success code with an error in the body.
Rule 160: Return the Last-Modified header for the newly created resource
Rule 161: Return the ETag header for the newly created resource. ETag can be generated using MD5 hash value
Rule 162: For bulk creation performed synchronously, return the HTTP status code (200 OK) and return a collection of the newly created URLs.
Rule 163: For bulk posting performed synchronously (for unsafe non-CRUD actions), return the HTTP status code (200 OK) and return in the response body a collection of the execution results.
Rule 164: For long processing activities (such as creating a huge bulk of objects), support async processing by using a special purpose resource (let's call it /async-tasks). Upon receiving a POST request, create a new resource of /async-tasks and return the HTTP status code (202 Accepted) along with a Location header that includes the URL for the newly created /async-tasks object. For example:
#Request
POST /users/async-tasks
{
"Users": []
}
#Response
HTTP/1.1 202 Accepted
Content-Type: application/json
Location: /users/async-tasks/1
Rule 165: For Async processing /async-tasks resource must support GET request (refer to GET HTTP method rules for a full list of applicable rules) to allow the consumer to inquire about the current state of the original request and should return one of the following:
- For still processing state: Return HTTP status code (200 OK) with a response payload indicating that the request is still under processing
- For successfully completed state: Return HTTP status code (303 See Other) and Location header pointing for the creation results.
- Return the HTTP status code (200 OK) for a failed processing state with an error report indicating the reason for the failure.
Rule 166: If request integrity is important, ask your client to send the signature in the header and make sure to validate it (see Rule#81). If the signature is not provided or is not valid. return HTTP status code (403 Forbidden) and an error report in the response body describing the issue.
PUT HTTP Method Rules
Rule 167: Use PUT to update an existing object. Following example updates already exist user with id 121: PUT /users/121
Rule 168: insert a sub-object for an existing parent object. The following example inserts a new address under existing user id 121: PUT /users/121/addresses
Rule 169: If the consumer didn't provide or provided invalid or expired credentials (In the Authorization header), return the HTTP Status code (401 Unauthorized), and it is recommended to return the WWW-Authenticate header to tell the client what kind of authentication is expected
Rule 170: If the consumer provided un-authorized credentials, return HTTP status code (403 Forbidden)
Rule 171: if the requested resource (collection or object) doesn't exist and the server doesn't know if the resource was there before or not, return HTTP Status code (404 Not Found)
Rule 172: if the requested resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version.
Rule 173: if the requested object resource doesn't exist and the server knows it was there before, return (405 Gone). This is applicable in this case a logical delete mechanism is implemented at the level of the object.
Rule 174: if the requested resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 175: If the request has a payload but doesn't include a Content-Type header. Return status code (400 Bad Request).
Rule 176: If the request has an unsupported media type defined in the Content-Type header, return status code (415 Unsupported Media Type)
Rule 177: If the request has a body, but it doesn't match the media type defined in the Content-Type header, return status code (400 Bad Request)
Rule 178: If the request has a body and it matches the media type defined in the Content-Type header, but the message structure or values are invalid, return status code (400 Bad Request)
Rule 179: in case of abnormal failures, return HTTP status code 5XX
Rule 180: in case of errors or failure, return an error report in the response body describing the issue.
Rule 181: Return status code (204 No Content) for a successful response and don't return a payload in the response body
Rule 182: Avoid returning a success code with an error in the body.
Rule 183: Return Last-Modified header to inform the consumer of the new last update date time
Rule 184: Return the ETag header to inform the consumer about the latest version of the resource. ETag can be generated using MD5 hash value
Rule 185: Allow conditional PUTrequest by supporting If-Unmodified-Since and If-Match headers to prevent concurrent update conflicts:
- The client can propagate If-Unmodified-Since with previously retrieved Last-Modified Or can propagate If-Match with previously retrieved ETag
- The server validates the value; if the condition is true, that means the resource has not been updated, there is no conflict, and the server must return HTTP Status Code (204 No Content)
- If the condition is false, that means the resource is updated, and there is a conflict. In this case, the server must return the HTTP Status code (412 Precondition Failed) along with the updated values of ETag and Last-Modified headers.
Rule 186: If request integrity is important, ask your client to send the signature in the header and make sure to validate it (see Rule#81). If the signature is not provided or is not valid. return HTTP status code (403 Forbidden) along with an error report in the response body describing the issue
DELETE HTTP Method Rules
Rule 187: Use DELETE to delete an existing object. The following example deletes already existing user with id 121: DELETE /users/121
Rule 188: If the consumer didn't provide or provided invalid or expired credentials (In the Authorization header), return the HTTP Status code (401 Unauthorized), and it is recommended to return the WWW-Authenticate header to tell the client what kind of authentication is expected
Rule 189: If the consumer provided un-authorized credentials, return HTTP status code (403 Forbidden)
Rule 190: if the requested resource (collection or object) doesn't exist and the server doesn't know if the resource was there before or not, return HTTP Status code (404 Not Found)
Rule 191: if the requested resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version.
Rule 192: if the requested object resource doesn't exist and the server knows it was there before, return (405 Gone). This is applicable in this case a logical delete mechanism is implemented at the level of the object.
Rule 193: if the requested resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 194: If the request has a payload but doesn't include a Content-Type header. Return status code (400 Bad Request).
Rule 195: If the request has an unsupported media type defined in the Content-Type header, return status code (415 Unsupported Media Type)
Rule 196: If the request has a body but it doesn't match the media type defined in the Content-Type header, return status code (400 Bad Request)
Rule 197: If the request has a body and it matches the media type defined in the Content-Type header, but the message structure or values are invalid, return status code (400 Bad Request)
Rule 198: in case of abnormal failures, return HTTP status code 5XX
Rule 199: in case of errors or failure, return an error report in the response body describing the issue.
Rule 200: Return status code (204 No Content) for a successful response and don't return a payload in the response body
Rule 201: Avoid returning the success code with an error in the body.
Rule 202: Return Last-Modified header to inform the consumer of the new last update date time
Rule 203: Allow conditional DELETE requests by supporting If-Unmodified-Since and If-Match headers to prevent deletion based on an invalid state:
- The client can propagate If-Unmodified-Since with previously retrieved Last-Modified Or can propagate If-Match with previously retrieved ETag
- The server validates the value; if the condition is true, that means the resource has not been updated, there is no conflict, and the server must return HTTP Status Code (204 No Content)
- If the condition is false, that means the resource is updated, and there is a conflict. In this case, the server must return the HTTP Status code (412 Precondition Failed)
Rule 204: If request integrity is important, ask your client to send the signature in the header and make sure to validate it (see Rule#81). If the signature is not provided or is not valid. return HTTP status code (403 Forbidden) along with an error report in the response body describing the issue
PATCH HTTP Method Rules
Rule 205: Use PATCH to update an existing object partially. Following example updates already exist user with id 121: PATCH /users/121
Rule 206: PATCH is relatively new and may not yet be supported by old tools and network devices.
Rule 207: With PATCH, it is recommended to use the standard patch format RFC6902. For example:
{ "op": "add", "path": "/a/b/c", "value": "foo" }
Rule 208: Advertise support for the PATCH via the allow header of the OPTIONS response.
#Request
OPTIONS /customers/1
#Response
HTTP/1.1 204 No Content
Allow: POST, GET, PATCH
Accept-Patch: application/json
Rule 209: If the consumer didn't provide or provided invalid or expired credentials (In the Authorization header), return the HTTP Status code (401 Unauthorized), and it is recommended to return the WWW-Authenticate header to tell the client what kind of authentication is expected
Rule 210: If the consumer provided un-authorized credentials, return HTTP status code (403 Forbidden)
Rule 211: if the requested resource (collection or object) doesn't exist and the server doesn't know if the resource was there before or not, return the HTTP Status code (404 Not Found)
Rule 212: if the requested resource doesn't exist and the server knows that the resource was there before, return the HTTP Status code (405 Gone). This is applicable in case an old version of API has been removed, and the server doesn't know if there is a newer version.
Rule 213: if the requested object resource doesn't exist and the server knows it was there before, return (405 Gone). This is applicable in this case a logical delete mechanism is implemented at the level of the object.
Rule 214: if the requested resource has moved to a different location, return the HTTP Status code (301 Moved Permanently) with the Location header value pointing to the new URI. This applies if a client requests an old API version and the server knows where the new version is (see Rule#25).
Rule 215: If the request has a payload but doesn't include a Content-Type header. Return status code (400 Bad Request).
Rule 216: If the request has an unsupported media type defined in the Content-Type header, return status code (415 Unsupported Media Type)
Rule 217: If the request has a body but it doesn't match the media type defined in the Content-Type header, return status code (400 Bad Request)
Rule 218: If the request has a body and it matches the media type defined in the Content-Type header, but the message structure or values are invalid, return status code (400 Bad Request)
Rule 219: in case of abnormal failures, return HTTP status code 5XX
Rule 220: in case of errors or failure, return an error report in the response body describing the issue(s).
Rule 221: Return status code (204 No Content) for a successful response and don't return a payload in the response body
Rule 222: Avoid returning a success code with an error in the body.
Rule 223: Return Last-Modified header to inform the consumer of the new last update date time
Rule 224: Return the ETag header to inform the consumer about the latest version of the resource. ETag can be generated using MD5 hash value
Rule 225: Allow conditional PUTrequest by supporting If-Unmodified-Since and If-Match headers to prevent concurrent update conflicts:
- The client can propagate If-Unmodified-Since with previously retrieved Last-Modified Or can propagate If-Match with previously retrieved ETag
- The server validates the value; if the condition is true, that means the resource has not been updated, there is no conflict, and the server must return HTTP Status Code (204 No Content)
- If the condition is false, that means the resource is updated, and there is a conflict. In this case, the server must return the HTTP Status code (412 Precondition Failed) along with the updated values of ETag and Last-Modified headers.
Rule 226: If request integrity is important, ask your client to send the signature in the header and make sure to validate it (see Rule#81). If the signature is not provided or is not valid. return HTTP status code (403 Forbidden) along with an error report in the response body describing the issue
REST API Design Process
- Find the nouns in the user story and model them as Resources
- Assign the Resources unique URIs. Use URL naming rules
- For Resources that can exist only as part of another Resource, define them as sub-resources. For example: /users/{user-id}/addresses
- Build schemas for the resource representations using JSON Schema and/or XML Schema. Use Data Model Rules
- Define the relationships between the models
- Build Hyper-medias to link the resources and include them in the resources schemas. Use Hypermedia rules.
- Find the verbs in the user story, try to map them to the CRUD operations, and associate them with the resources. Use POST Method Rules
- Find the non-CRUD verbs and define them as sub-resources. Use POST Method Rules
- Build Hyper-medias for workflows activities and include them in the schemas
- Build Hyper-medias for pagination and include them in the schemas
- Build a special-purpose resource for Async Tasks (/async-tasks)
- Describe the API using Open API Specs and use $ref to include the representations schemas
References
The above list of rules has been collected and consolidated from the following references:
- www.arcitura.com
- RESTful Web APIs, by Leonard Richardson
- REST API Design Rulebook, by Mark Masse
- Designing Web APIs - Building APIs that developers love, by Brenda Jin
- Restful Web Services Cookbook, by Subbu Allamaraju
- W3C Activity Stream Vocabulary
- Dublin Core
- Schema.org
- Microformats Wiki
- http://xmlns.com/foaf/spec/
- https://tools.ietf.org/html/rfc8288
- https://www.iana.org/assignments/link-relations/link-relations.xhtml
- https://tools.ietf.org/html/rfc6570
- https://tools.ietf.org/html/rfc7807
- https://tools.ietf.org/html/rfc6906
- https://tools.ietf.org/html/rfc5005
- https://tools.ietf.org/html/rfc4685
- https://tools.ietf.org/html/rfc5829
- https://tools.ietf.org/html/rfc7946
- https://tools.ietf.org/html/rfc6902
- https://tools.ietf.org/html/rfc3339
- https://www.iso.org/iso-3166-country-codes.html
- https://en.wikipedia.org/wiki/ISO_4217
- https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-deprecation-header-02
- https://www.rfc-editor.org/rfc/rfc8594.html
- https://www.rfc-editor.org/rfc/rfc6648
About the Creator
Haitham Raik
I have 20 years of experience in Software development and Software Architecture. In this page, I will share with you topics based on my expertise related to software development and architecture
Comments
There are no comments for this story
Be the first to respond and start the conversation.