Best Practices for Better RESTful API with Example

Best Practices for Better RESTful API with Example

Web APIs has become a very important topic in the last couple of years. 

APIs give you freedom to change your backend system without affecting your stockholders that depends on your resources therefore it is important to design a clean API.

Typically we use a RESTful design for our web APIs. The concept of REST is to separate the API structure into logical resources. There are used the HTTP methods GET, DELETE, POST and PUT to operate with the resources. 

My goal with this post is to describe best practices to design today's web applications. 

Just like any UI, an API is a developer's UI. So, it is important to ensure the user's experience is thought out carefully.

Some basic requirement that the API must have

  • It should be developer (API Consumer) friendly
  • It should be web standards and flexible to power majority of the UI
  • It should be simple, intuitive and consistent to make adoption not only easy but pleasant
  • It should be efficient, while maintaining balance with the other requirements

RESTful URLs and actions

The fundamental concept in any RESTful API is the resource. A resource is an object with a type, associated data, relationships to other resources, and a set of methods that operate on it. It is similar to an object instance in an object-oriented programming language, with the important difference that only a few standard methods are defined for the resource (corresponding to the standard HTTP GET, POST, PUT and DELETE methods), while an object instance typically has many methods.

What can be a resource?

It is very important to identify resources from your application. These should be noun not verb that make sense for end API consumers. Don't miss up singular and plural nouns. Keep it simple and use only plural nouns for all resources.

For example if you are working with catalog system then the possible resource name will be 

  • products - Get a collection of all products
  • products/{id} - Get a details of a specific product

If a resource is related to another resource use subresources.

  • /products/{id}/images - Get a collection of images associated with a specific product
  • /products/{id}/images/{id} - Get a specific image associated with a specific product
  • /products/{id}/features - Get a collection of features associated with a specific product
  • /products/{id}/features/{id} - Get a particular feature associated with a specific product

HTTP Methods

Lots of time REST works on resources in a CRUD fashion, however do not get lulled into a false sense that REST is only about CRUD. REST can be used to perform actions that indirectly operate on resources.

  • GET - Retrieve the records
  • POST - Create new record
  • PUT - Update an existing record
  • DELETE - Delete a record

GET method and query parameters should not write the state

Use PUT, POST and DELETE methods instead of the GET method to alter the state.

Do not use GET for state changes:

GET /products/675?status=1 or
GET /products/769/status/activate

Use HTTP headers for serialization formats

The format has to be specified in the HTTP-Header. Content-Type defines the request format. Accept defines a list of acceptable response formats. Both, client and server, need to know which format is used for the communication. 

Use Http headers for Authorization

A RESTful API should be stateless. This means that request authentication should not depend on cookies or sessions. Instead, each request should come with some sort of authentication credentials. Send authorization token in HTTP-Header Authorization.

Result filtering, sorting, field selection and pagination for collections

Filtering:

GET /products?filters[status_id]=1&filters[category_id]=23&filter[features][id]=5

Sorting:

GET /products?sort=-product_id  //product_id wise descending order
GET /products?sort=+product_id  //product_id wise ascending order
GET /products?sort=+product_id,-created_on  //Retrieves a list of products in ascending order of product_id. Within a specific product_id, newer products are ordered first

Searching:

GET /products?filters[q]=iphone 7.   //Searching by q (Full text search)

Field Selection:

GET /products?fields=product_id,status_id,feature[id]

Pagination:

GET /products?page=3&per_page=50

HTTP status code in Response

There are many meaningful status codes defined by HTTP. It must be returned from your API. On the basis of response code received by your API, the API caller can route their responses accordingly. I prepared a list of HTTP status code mostly used in API response.

200 – OK – Response to a successful GET, PUT or DELETE. It can also be used for a POST that doesn't result in a creation.
201 – OK – New resource has been created
204 – OK – The resource was successfully deleted
304 – Not Modified – The client can use cached data
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“
401 - Unauthorized - The request requires an user authentication
403 - Forbidden - The server understood the request, but is refusing it or the access is not allowed.
404 - Not found - There is no resource behind the URI.
405 - Method Not Allowed - When an HTTP method is being requested that isn't allowed for the authenticated user
410 - Gone away - 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 - 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.
429 - Too Many Requests - When a request is rejected due to rate limiting
500 – Internal Server Error – API developers should avoid this error. 

Overriding the HTTP method

Some HTTP clients can only work with simple GET and POST requests. To support a RESTful API with these limitations, the API needs a way to override the HTTP method. Use the custom HTTP Header X-HTTP-Method-Override to override the POST Method for PUT or DELETE.

Example of GET API - Get a collection of products

End Point: http://somehostname.com/api/v1/products
R?equest Method : GET

Success:

{
  "responseheader":
  {
    "status":1,
    "http_code":200,
    "msg_code":1,
    "msg_desc":"Some Relevant message."
    
  },  
  "data":
  [
    {
      "product_id":53435,
      "product_title":"Title 1 of the product",
      "shipping_price":15.50
    },
    {
      "product_id":53435,
      "product_title":"Title 1 of the product",
      "shipping_price":15.50
    }
  ],
  "extra":
  {
    "time_taken":0.02,
    "count":5000,
    "paging":
    {
      "first": "https://somehostname.com/api/v1/products?page=1",
      "last": "https://somehostname.com/api/v1/products?page=5719",
      "previous": "",
      "next": "https://somehostname.com/api/v1/products?page=2"
    }
  }
}

Error:

{
"responseheader":
  {
    "status":0,
    "http_code":200,
    "msg_code":102,
    "msg_desc":"Something wrong."
    
  },  
  "data":
  [],
  "extra":
  {}
}

Example of POST API - Create a new product

End Point: http://somehostname.com/api/v1/products
R?equest Method : POST

Request Body [Multiple document for bulk product creation]:

{
  "data": [
    {
      "reference_id": "ref1",
      "product_title": "Title 1 of product",
      "shipping_price": 15.5,
      "price": 120
    },
    {
      "reference_id": "ref2",
      "product_title": "Title 2 of product",
      "shipping_price": 15.5,
      "price": 125
    }
  ]
}

Success:

{
  "responseheader": {
    "status": 1,
    "http_code": 201,
    "msg_code": 20,
    "msg_desc": "Successfully Created"
  },
  "data": [
    {
      "has_error": false,
      "reference_id": "ref1",
      "product_id": 8988789
    },
    {
      "has_error": false,
      "reference_id": "ref2",
      "product_id": 8988790
    }
  ],
  "extra": {}
}

Partial Success:?

{
  "responseheader": {
    "status": 2,
    "http_code": 201,
    "msg_code": 22,
    "msg_desc": "Partial Success"
  },
  "data": [
    {
      "has_error": false,
      "reference_id": "ref1",
      "product_id": 8988789
    },
    {
      "has_error": true,
      "reference_id": "ref2",
      "messages": {
        "errors": [],
        "warnings": [],
        "notifications": []
      }
    }
  ],
  "extra": {}
}

Error:

{
  "responseheader": {
    "status": 0,
    "http_code": 200,
    "msg_code": 23,
    "msg_desc": "Unsuccessful"
  },
  "data": [
    {
      "has_error": true,
      "reference_id": "ref2",
      "messages": {
        "errors": [],
        "warnings": [],
        "notifications": []
      }
    },
    {
      "has_error": true,
      "reference_id": "ref2",
      "messages": {
        "errors": [],
        "warnings": [],
        "notifications": []
      }
    }
  ],
  "extra": {}
}

Similarly we can use for update and delete in bulk.

nice blog.. Specially the format you shared for bulk creation.

回复

要查看或添加评论,请登录

Vikas Singh的更多文章

社区洞察

其他会员也浏览了