RESTful API

REST Maturity

The maturity of RESTful API is based on the following levels. In practice, most of the web APIs sit on level 2:

  • Level 0: Define one URI, and all operations are POST requests to this URI.
  • LEVEL 1: Create separate URIs for each resource but use POST for communicating to the endpoints.
  • LEVEL 2: Use HTTP methods to define operations on resources (POST, GET, PUT, DELETE)
  • LEVEL 3: Use hypermedia controls (HATEOAS) which includes the link for the next operation on the response.

Naming Resources

When possible, resource URIs should be nouns (the resource) and not verbs (the operations on the resource).

https://soheilvaseghi.com/articles       // Good
https://soheilvaseghi.com/create-arcicle // Avoid

Use plural nouns for URIs that return collections and add the identifier to the path for getting an individual resource. Avoid requiring resource URIs more complex than collection/item/collection.

/customers                        // All customers
/customers/5                      // Single customer
/customers/5/orders               // Orders of a customer
/customers/5/orders/99/products   // Too complex. Avoid

Avoid "chatty" APIs which send multiple requests to find all of the data it requires. Instead, you might want to denormalise the data and combine related information into bigger resources that can be retrieved with a single request.

Methods

  • GET retrieves a representation of the resource and it must be idempotent. It doesn't have a body and all the request objects should be in the query string or path. It can be cached by CDNs.
  • POST creates a new resource or triggers an action on the server. It has body but CDNs cannot cache POST requests. It is not guaranteed to be idempotent.
  • PUT either creates or replaces the resource at the specified URI. PUT is for a known resource, and therefore is used for updating. Must be idempotent.
  • PATCH performs a partial update of a resource. The request body specifies the set of changes to apply to the resource. When you want to replace an existing resource entirely, then you can use PUT. When you want to update a single field of the resource, then sending the complete resource representation might be cumbersome and utilises a lot of unnecessary bandwidth. In such cases, using PATCH make a lot more sense. It is not guaranteed to be idempotent.
  • DELETE removes the resource at the specified URI. Must be idempotent.
Resource POST GET PUT DELETE
/customers Create a new customer Retrieve all customers Bulk update of customers Remove all customers
/customers/1 Error Retrieve the details for customer 1 Update the details of customer 1 if it exists Remove customer 1
/customers/1/orders Create a new order for customer 1 Retrieve all orders for customer 1 Bulk update of orders for customer 1 Remove all orders for customer 1

Idempotency is the property of certain operations in mathematics and computer science whereby they can be applied multiple times without changing the result beyond the initial application. PUT, DELETE and GET HTTP methods should be implemented in an idempotent manner according to the HTTP standard and running these commands multiple times should not be different to run them only once.

Content-Type Header

In response tells the client what the content type of the returned content actually is. and in requests, (such as POST or PUT), the client tells the server what type of data is actually sent.

The HTTP POST method sends data to the server. The type of the body of the request is indicated by the Content-Type header. Values could be:

  • application/x-www-form-urlencoded: the keys and values are encoded in key-value tuples separated by '&', with a '=' between the key and the value. Non-alphanumeric characters in both keys and values are percent-encoded: this is the reason why this type is not suitable to use with binary data (use multipart/form-data instead)
  • multipart/form-data: each value is sent as a block of data ("body part"), with a user agent-defined delimiter ("boundary") separating each part. The keys are given in the Content-Disposition header of each part.
  • application/json: for json text
  • text/plain

ASP.Net Web API does not support multiple parameters, for sending multiple parameters either use query strings or define a route in which parameters are separated by / or put all the parameters inside one object.

HTTP Status Codes

GET

A successful GET method typically returns 200 (OK). If the resource cannot be found, the method should return 404 (Not Found).

POST

  • If a POST method creates a new resource, it returns 201 (Created). The URI of the new resource is included in the Location header of the response. The response body contains a representation of the resource.
  • If a POST method does some processing but does not create a new resource, the method can return 200 and include the result of the operation in the response body. If there is no result to return, the method can return 204 (No Content) with no response body.
  • If the client puts invalid data into the request, the server should return 400 (Bad Request). The response body can contain additional information about the error.

PUT

If a PUT method creates a new resource, it returns 201 (Created), as with a POST method. If the method updates an existing resource, it returns either 200 (OK) or 204 (No Content). In some cases, it might not be possible to update an existing resource. In that case, consider returning 409 (Conflict).

DELETE

If the delete operation is successful, the web server should respond with 204 (No Content), indicating that the process has been successfully handled, but that the response body contains no further information. If the resource doesn't exist, the web server can return 404 (Not Found).

Asynchronous Operations

Sometimes a POST, PUT, PATCH, or DELETE operation might require processing that takes a while to complete. If you wait for completion before sending a response to the client, it may cause unacceptable latency. If so, consider making the operation asynchronous. Return HTTP status code 202 (Accepted) to indicate the request was accepted for processing but is not completed.

You should expose an endpoint that returns the status of an asynchronous request, so the client can monitor the status by polling the status endpoint. Include the URI of the status endpoint in the Location header of the 202 (Accepted) response. For example:

HTTP/1.1 202 Accepted
Location: /api/status/12345

If the client sends a GET request to this endpoint, the response should contain the current status of the request. Optionally, it could also include an estimated time to completion or a link to cancel the operation.

HTTP/1.1 200 OK
Content-Type: application/json

{
    "status":"In progress",
}

REST vs RPC

Operation RPC REST
Signup POST /signup POST /persons
Resign POST /resign {"personId": "1234"} DELETE /persons/1234
Get a person GET /getPerson?personId=1234 GET /persons/1234
Get a person's items GET /getPersonItems?personId=1234 GET /persons/1234/items
Add an item to person's items POST /addItemsToUserItems {"personId": "1234", "itemId": "456" } POST /persons/1234/items {"itemId": "456"}
Update an item POST /modifyItem {"itemId": "456", "key": "value" } PUT /items/456 {"key": "value"}
Delete an item POST /removeItem {"itemId": "456"} DELETE /items/456