Best practices for REST API design

This blog post discusses some of the best practices (which we used across our projects) in designing REST APIs. REST is a philosophy not a standard, so following certain best practices will help you get right and identify usage issues upfront. Though the practices are exhaustive I will try to cover the most important ones.

Use HTTP verbs

Never build your API with just POST. Technically it does the job but clearly violates REST philosophy. Each HTTP verb has a meaning and usage semantics.

GET – Fetch information. This should not change the state of the system.
POST – Create content.
PUT – Update content.
DELETE – Delete
and more.

Always think as resources

RESTful API provides access to your system. Your system is a collection of resources. Thinking in terms of resources simplifies the signature of endpoints and makes usage more intuitive.

Bad
GET /users/info?id=<id>
POST /posts/create
DEL /posts/delete?id=<id>
Good
GET /users/:id - Fetches user profile
POST /posts - Creates a new post
PUT /posts/:id - Updates a specific post
DEL /posts/:id - Deletes an existing post
Sub resources can be accessed as 
GET /posts/:id/comments - Fetches comments for the specified post.
  • The usage of correct HTTP verbs convey the usage of endpoint. This mostly eliminates actions to be part of the endpoint and the API feels intuitive.
  • It makes more sense to use plural names for resources.
  • Once an endpoint is finished, put yourself in the developer’s shoes and read it yourself. It should feel right and natural. A peer-to-peer review of API signature is highly recommended because once your API signature is published you cannot make any further changes.

Use versioning, but don’t overuse

GET https://api.example.com/1/users/:id
  • Versioning helps in faster API iterations and support of legacy APIs.
  • Don’t overuse versioning. Increment versioning only when your API is breaking an existing contract and not when new APIs are being added. It is a good practice to increment versioning when the complete set of APIs are updated.
  • Older API versions are supposed to be deprecated as soon as possible. Never put your clients in a situation where they should use multiple API versions.
  • If you end up changing your versions within 6 months then there is something fundamentally flawed with your API strategy.

SSL

Always, always, always use SSL and sleep peacefully. Do not add SSL support just the day before shipping to production. Start with SSL and test it all through the development phase.

JSON please!

JSON is the preferred data exchange format. It is readable, simple to parse and easy to convert to data models.

Explicitly support and expect Content-Type header be set to application/json

Request

  • Prefer JSON for input rather than URL encoded parameters.
  • JSON request body helps to express more complex requests. For example the following example makes it easy in JSON input rather than in URL encoding. Also JSON parsing on Server side is straight forward.
POST /posts
{
 “title”: “10 best food outlets in Singapore”,
 “content”: “…”,
 “tags”: [“food”, “life”, “singapore”]
}

Response

When your API server returns a response (success/error) it is received, parsed and consumed by your clients. So design your response structures thinking in terms of clients and making it easy for them to consume.

  • Response is always a binary outcome. Either success or error, there is no middle ground. Period.
  • Define a success and error JSON structure upfront and stick to it.
    All API responses should fit in either of these two structures. This will help clients to write a well defined parser.

An example success response

{
 “ok”: true,
 “result”: {…}
}

An example error response

{
 “ok”: false,
 “error”: {…}
}

Here ok is the decision branch between success and error response parsing (along with the correct HTTP status code).

  • Define a simple structure by reducing hierarchy at root level. This will simplify parsing.
  • This structure is parsed by a top level response parsers and the contents of either result/error are delegated down the line. The top level parsers are not concerned about the contents inside the response.

Tell me more about errors

An error should tell more about itself. Don’t be cryptic.

Bad error response

{
 “ok”: false,
 “error”: {
        “code”: 1809,
        “message”: “Invalid API request”
       }
}

Your error response should not frustrate your developer. Instead it should help identify the problem.

Good error response

{
 “ok”: false,
 “error”: {
      “code”: 1809,
      “message”: “Invalid query parameter user_id”,
      “usage”: “https://docs.myapp.api/users/query” 
      }
}
  • message is not supposed to be shown to the User. It is internal to developer. If a user message is required provide it with a separate keyword and make sure it is i18n supported for different languages.
  • Optionally include server dumps in debug builds so that your developers know what and where went wrong. These are invaluable for bug reporting.

Use HTTP codes.

  • Convey correct HTTP codes with the respective responses. Don’t use 200 all across!
    For example return 201 for successfully creating new content. Return 200 for successfully fetching the complete content.
  • Error responses should  be responded with a valid HTTP status code. Don’t just return HTTP 500 for everything.

Use consistent keywords

  • Keywords are a set of keys used in REST API’s request and response communication.
  • Keeping the supported keyword set small is important for maintainable code.
  • Don’t introduce new keywords. See if existing keywords can convey the meaning.
    For example don’t use count at one place and size at another. Be consistent.

Identify clients

A RESTful API is a means to access your system of resources. So identifying who is accessing is important.

  • Provide API keys to all your clients including your official in-house apps.
  • Each client should have a different API key.
  • Every request should have an API key as part of the request header. Return 4o3 HTTP code if it fails to do so.
  • If a client is compromised, black-list the specific API key and continue with a contingency plan.
  • API keys also helps in providing usage insights and volume of API request.

Authenticate or Authorise ?

Authentication is stating that you are who you are. Authorisation is allowing to do what you are supposed to do.

If you are a contained system which requires User to login/signup to perform certain tasks (like creating new content), then you are authenticating.

  • Authentication token should be generated (or regenerated) on successful authentication. The same should be returned to the client. This establishes a session.  JSON Web Tokens are even more secured.
  • Subsequent API calls should specify this session token as part of request header. This helps in identifying the User who is performing the task.
  • Have a mechanism to revoke and regenerate session tokens.
  • Identify which resources needs authentication and document them.

Incase you are a system that serves 3rd party clients to access your Users on their behalf, then you should use OAuth.

Limit, sort and filter

Your API should provide developers just the data they need. This makes smart usage of the bandwidth.

  • When providing a list of items as response, limit them to a meaningful number and support sorting on different fields. Let clients request for more by supporting pagination. Support HATEOAS if you can!
  • Let client specify which fields they want want as part of response.

 

Fetch 5 posts with partial data
 GET /posts?fields=title,date&limit=5&sort=date

Documentation

Your API documentation should be as good as your code because your developer looks into the docs and understands how to use the API.

  • Docs should be clear and include usage examples.
  • They should be updated regularly to include any deprecated endpoints.

Also referring to a good RESTful API implementation is a recommended way to learn more.  Some of the good REST API references to start with

Instagram API
Twitter API

Though most of these items are a matter of common sense, I did see developers depart due to project pressures and poor understanding of concepts. Problem is – your API once released is like writing something on stone, you cannot change it and have to live with it finally ending up supporting it. Better get it right when the time is right.

 

Advertisements

4 thoughts on “Best practices for REST API design

  1. On successful authentication, what is the right way to send the

    auth_token

    to the user and why?

    auth_token

    returned inside the response headers

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin →*
    Connection →keep-alive
    Content-Length →112
    Content-Type →application/json; charset=utf-8
    Date →Tue, 09 Aug 2016 10:01:24 GMT
    Server →nginx/1.4.6 (Ubuntu)
    X-Powered-By →Express
    sessiontoken →RA1wkDY2Ee8wcGDQ8tLU1ULn
    

    OR inside the response body

    {
      "ok": true,
      "result": {
        "user": {
          "name": "Test User"
        },
        "auth_token": "asdskfjkkkknfmdnf"
      }
    }
    
    1. Since the login authenticates the User, Server sends the result as part of response body. This includes the tokens without which the response is incomplete. The token is the Server’s way of saying – “Hey, just use this token so that I can identify the User you are serving to”. It is not a meta information, but an actual contract between the client and server.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s