REST API
Table of Contents
Best Practices
URLs and Actions
- Use always plural keywords for consistency.
To prevent an API consumer from having to hit the API again for an updated representation, have the API return the updated (or created) representation as part of the response. In case of a POST that resulted in a creation, use a HTTP 201 status code and include a Location header that points to the URL of the new resource.
GET /tickets
- Retrieves a list of tickets
GET /tickets/12
- Retrieves a specific ticket
POST /tickets
- Creates a new ticket
PUT /tickets/12
- Updates ticket #12
PATCH /tickets/12
- Partially updates ticket #12
DELETE /tickets/12
- Deletes ticket #12
GET /tickets/12/messages
- Retrieves list of messages for ticket #12
GET /tickets/12/messages/5
- Retrieves message #5 for ticket #12
POST /tickets/12/messages
- Creates a new message in ticket #12
PUT /tickets/12/messages/5
- Updates message #5 for ticket #12
PATCH /tickets/12/messages/5
- Partially updates message #5 for ticket #12
DELETE /tickets/12/messages/5
- Deletes message #5 for ticket #12
PUT /gists/:id/star
- Use sub-resource for actions don't fit CRUD
DELETE /gists/:id/star
- Use sub-resource for actions don't fit CRUD
GET /tickets?state=open
- Filter
GET /tickets?sort=-priority
- Retrieves a list of tickets in descending order of priority
GET /tickets?sort=-priority,created_at
- Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first
GET /tickets?sort=-updated_at
- Retrieve recently updated tickets
GET /tickets?state=closed&sort=-updated_at
- Retrieve recently closed tickets
GET /tickets?q=return&state=open&sort=-priority,created_at
- Retrieve the highest priority open tickets mentioning the word 'return'
GET /tickets/recently_closed
- Aliases for common queries
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
Header
- Link
- Pagination
- X-Rate-Limit-Limit
- The number of allowed requests in the current period
- X-Rate-Limit-Remaining
- The number of remaining requests in the current period
- X-Rate-Limit-Reset
- The number of seconds left in the current period
- ETag
- Caching
- Last-Modified
- Caching
JSON
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#json-responses
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#json-requests
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#snake-vs-camel
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#pretty-print-gzip
- https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#envelope
- http://ieeexplore.ieee.org/document/5521745/?reload=true&tp=&arnumber=5521745
- Use JSON where possible for both input and output
snake_case
is preferable tocamelCase
snakecase is 20% easier to read than camelCase! Many popular JSON APIs use snakecase following the underlying language.- Pretty print by default & ensure gzip is supported The cost of the extra data transfer is negligible gzipping in itself provided over 60% in bandwidth savings.
- Don't use response envelopes(
{"data":{<actual result>}}
by default Within envelopes, it is easy to include meta data into the payload. However, thanks toCORS
,LINK
header, etc., it is no more mandatory.
Errors and status codes
{
"code" : 1234,
"message" : "Something bad happened :(",
"description" : "More details about the error here"
}
{
"code" : 1024,
"message" : "Validation Failed",
"errors" : [
{
"code" : 5432,
"field" : "first_name",
"message" : "First name cannot have fancy characters"
},
{
"code" : 5622,
"field" : "password",
"message" : "Password cannot be blank"
}
]
}
200
OK- Response to a successful GET, PUT, PATCH or DELETE. Can also be used for a POST that doesn't result in a creation.
201
Created- Response to a POST that results in a creation. Should be combined with a Location header pointing to the location of the new resource
204
No Content- Response to a successful request that won't be returning a body (like a DELETE request)
304
Not Modified- Used when HTTP caching headers are in play
400
Bad Request- The request is malformed, such as if the body does not parse
401
Unauthorized- When no or invalid authentication details are provided. Also useful to trigger an auth popup if the API is used from a browser
403
Forbidden- When authentication succeeded but authenticated user doesn't have access to the resource
404
Not Found- When a non-existent resource is requested
405
Method Not Allowed- When an HTTP method is being requested that isn't allowed for the authenticated user
410
Gone- Indicates that the resource at this end point is no longer available. Useful as a blanket response for old API versions
415
Unsupported Media Type- If incorrect content type was provided as part of the request
422
Unprocessable Entity- Used for validation errors
429
Too Many Requests- When a request is rejected due to rate limiting
Versioning
For browser explorability, it is generally better to put the major verion in URI.
I'm a big fan of the approach that Stripe has taken to API versioning - the URL has a major version number (v1), but the API has date based sub-versions which can be chosen using a custom HTTP request header.
Topics
Response of PUT
- Updated
200 OK
(or 204 No Content), no body needed- Created
201 Created
, Location header, URI and metadata as body- Conflict
409 Conflict
, list of differences as a body- Failed
400 Bad Request
, body contains explanation in a natual language
Visibility
- Allamaraju, Subbu. RESTful Web Services Cookbook: Solutions for Improving Scalability and Simplicity (p. 5). O'Reilly Media. Kindle Edition.
For RESTful web services, your key goal must be to maintain visibility to the extent possible. Keeping visibility is simple. Use each HTTP method such that it has the same semantics as specified by HTTP, and add appropriate headers to describe requests and responses.
Whenever you have multiple resources that share data or whenever an operation modifies more than one resource, be prepared to trade visibility for better abstraction of information, loose coupling, network efficiency, resource granularity, or pure client convenience.
POST vs. PUT vs. PATCH
POST
- server will decide the URI for the newly created resource.
PUT
- client is determining the URI for the newly created resource.
- The
PUT
method requests that the enclosed entity be stored under the supplied Request-URI.
PATCH
- when the client is sending one or more changes to be applied by the server.
- applies a delta rather than replacing the entire resource.
Is it okay to return JSON Array
- https://softwareengineering.stackexchange.com/questions/253897/api-always-returns-jsonobject-or-jsonarray-best-practices
- https://softwareengineering.stackexchange.com/questions/286293/whats-the-best-way-to-return-an-array-as-a-response-in-a-restful-api
- https://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk
- Some people say this is cleaner.
- But this is vulnerable to XSS in some old browsers.(modern browsers have addressed this problem) JSON object which starts its content with
{
is considered to be Javascript block. However, JSON array is a valid Javascript array.
- Pros
- can extend the result, like including
count
. - Cons
- less pretty
Overall: It is better to use enveloping arrays with objects.
Principles
- Stateless
- induces the properties of visibility, reliability, and scalability.
- Cache
- improves network efficiency
- Uniform Interface
- overall system architecture is simplified and the visibility of interactions is improved.
- Layered System
- improves behavior for Internet-scale requirements, we add layered system constraints
- Code-On-Demand
- allows client functionality to be extended by downloading and executing code in the form of applets or scripts.
- Request (a)
- a client proxy scenario
- Request (b)
- a direct request scenario
- Request (c)
- a server proxy scenario