top of page

How NOT to design APIs (so far as I know)

REST APIs have become a very important topic for the last few years. There are used the HTTP methods GET, DELETE, POST, PUT, PATCH to operate with the resources having an equivalent in CRUD. Where POST / GET is the oldest and used by browser forms or URL rewriting since IE4.

1. Use nouns in paths

For an easy understanding use this structure for every object. Do not mix up singular and plural nouns. Keep it simple and use only plural nouns for all resources. Its easier to remember.

/cars
/users
/products
/user-preferences
/settings

 

2. GET is not the only option for action

Use PUT, PATCH POST, and DELETE methods instead of the GET method to alter the state. These correspond to create, read, update, and delete (or CRUD) operations, respectively. There are a number of other verbs, too, but are utilized less frequently. Of those less-frequent methods, OPTIONS and HEAD are used more often than others.

3. Use HTTP headers for serialization formats

Both client and server, need to know which format is used for the communication. The format has to be specified in the HTTP-Headers. It good to know what format will be sent and received.

  1. Content-Type defines the request format (eg. application/json)

  2. Accept defines a list of acceptable response formats (eg. application/json)

I personally think that the best format is JSON because of spread of it self in computer languages and possible enhacements.

4. Use HATEOAS

Hypermedia as the Engine of Application State is a principle that hypertext links should be used to create a better navigation through the API.

{

"id": 1,
"name": "John",
"surname": "Skocdopole",
"relationship": [
 {
 "id": "23",
 "type": "love"
 "name": "Verka Skocdopole",
 "links": [{
   "rel": "self",
   "href": "/api/v1/users/23"
  }]
 }]

}
 

5. Provide filtering, sorting, field selection and paging for collections

5.1 Filtering:

Use a unique query parameter for all fields or a query language for filtering.

GET /users?surname=skocdopole <== Returns a list of users having Skocdopole surname
 

5.2 Sorting:

Allow ascending and descending sorting over multiple fields.

GET /users?sort=surname&sort=desc
 

This returns a list of employees sorted by descending surnames.

5.3 Field selection

Mobile clients display just a few attributes in a list. They don’t need all attributes of a resource. Give the API consumer the ability to choose returned fields. This will also reduce the network traffic and speed up the usage of the API.

GET /users?fields=name,surname
 

5.4 Paging

Use limit and offset. It is flexible for the user and common in leading databases. The default should be limit=20 (do we need more for the data sample?) and offset=0 because we would like to start from begining if these parameters are not defined.

GET /users?offset=0&limit=20
 

6. Version your API

Make the API Version mandatory and do not release an unversioned API. Use a simple ordinal number and avoid dot notation such as 2.5 or 2_5.

We are using the url for the API versioning starting with the letter „v“

/api/v1/users
 

7. Handle Errors with HTTP status codes

It is hard to work with an API that ignores error handling. Pure returning of a HTTP 500 with a stacktrace is not very helpful.

Use HTTP status codes

The HTTP standard provides over 70 status codes to describe the return values. We don’t need them all, but there should be used at least a mount of 10.

  1. 200 – OK – Eyerything is working

  2. 201 – OK – New resource has been created

  3. 204 – OK – The resource was successfully deleted

  4. 304 – Not Modified – The client can use cached data

  5. 400 – Bad Request – The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“

  6. 401 – Unauthorized – The request requires an user authentication

  7. 403 – Forbidden – The server understood the request, but is refusing it or the access is not allowed.

  8. 404 – Not found – There is no resource behind the URI.

  9. 422 – Unprocessable Entity – Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload.

  10. 500 – Internal Server Error – API developers should avoid this error. If an error occurs in the global catch blog, the stracktrace should be logged and not returned as response.

8. Use error payloads

All exceptions should be mapped in an error payload. Here is an example how a JSON payload should look like. We should also send a header having a code 404 to avoid address remembered in the browser.

{

    "internalMessage": "User doesnt exists.",
    "errorcode": 404
}
}  
bottom of page