REST API

What is REST

REST Methods

In a nutshell

  • GET should be Read Only i.e. have no effect on server state.

  • POST is used to create a resource on the server.

  • PUT is used to update or create a resource.

  • DELETE is used to delete a resource.

In other words, if your API action changes the server state, REST advises us to use POST/PUT/DELETE, but not GET.

Idempotent Actions

  • If your action is not idempotent, then you MUST use POST. If you don't, you're just asking for trouble down the line. GET, PUT and DELETE methods are required to be idempotent.

  • Imagine what would happen in your application if the client was pre-fetching every possible GET request for your service -- if this would cause side effects visible to the client, then something's wrong.

Design uniform REST APIs

In real world, an API is never going to be completely stable. So it’s important how this change is managed.

A well documented and gradual deprecation of API can be an acceptable practice for most of the APIs.

1. Handle Errors with HTTP status codes and Use error payloads

Consistent HTTP Status Codes in response (around 4xx and 5xx)

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.

200 – OK – Eyerything is working

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.

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 response from server

  • The HTTP 429 Too Many Requests response status code indicates the user has sent too many requests in a given amount of time ("rate limiting").

  • A Retry-After header might be included to this response indicating how long to wait before making a new request.

  • Receiving a status 429 is not an error, it is the other server "kindly" asking you to please stop spamming requests. Obviously,your rate of requests has been too high and the server is not willing to accept this.

  • Reference

  • The client could handling\prevent 429 with below options:

      1. Use multiple user-id to distribute traffic on load

      2. Sleep\Wait for retry-after time in 429 response

      3. Exponential backoff iff retry-after time is not provided by server response

      4. Token bucketing to ensure client requests are with-in rate-limit [Prevention]

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.

Use error payloads

All exceptions should be mapped in an error payload. Here is an example how a JSON payload should look like.

{

"errors": [

{

"userMessage": "Sorry, the requested resource does not exist",

"internalMessage": "No car found in the database",

"code": 34,

"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"

}

]

}

2. API Versioning

a. When to Version

APIs only need to be up-versioned when a breaking change is made. Breaking changes include:

- a change in the format of the response data for one or more calls

- a change in the response type (i.e. changing an integer to a float)

- removing any part of the API.

b. Breaking changes

Breaking changes should always result in a change to the major version number for an API or content response type.

c. Non-breaking changes

Non-breaking changes, such as adding new endpoints or new response parameters, do not require a change to the major version number.

However, it can be helpful to track the minor versions of APIs when changes are made to support customers who may be receiving cached versions of data or may be experiencing other API issues.

d. How-To Version

URI Versioning

Using the URI is the most straightforward approach (and most commonly used as well) though it does violate the principle that a URI should refer to a unique resource.

You are also guaranteed to break client integration when a version is updated.

The version need not be numeric, nor specified using the “v[x]” syntax.

Alternatives include dates, project names, seasons or other identifiers that are meaningful enough to the team producing the APIs and flexible enough to change as the versions change.

eg:

http://api.example.com/v1

http://apiv1.example.com

Versioning using Custom Request Header

A custom header (e.g. Accept-version) allows you to preserve your URIs between versions though it is effectively a duplicate of the content negotiation behavior implemented by the existing Accept header.

e.g.

Accept-version: v1

Accept-version: v2

Versioning using Accept header

Content negotiation may let you to preserve a clean set of URLs but you still have to deal with the complexity of serving different versions of content somewhere. This burden tends to be moved up the stack to your API controllers which become responsible for figuring out which version of a resource to send. The end result tends to be a more complex API as clients have to know which headers to specify before requesting a resource.

e.g.

Accept: application/vnd.example.v1+json

Accept: application/vnd.example+json;version=1.0

3. Response schema format (e.g. JSON only responses)

4. Response schema guidelines

e.g. Structure of the response json and keeping it flexsible to accomodate future changes

5. API naming convention

mvx/v1/submission

mvx/v1/status

6. Use nouns but no verbs

Use nouns

/cars

/cars/711

Do not use verbs:

/getAllCars

/createNewCar

/deleteAllRedCars

7. GET method and query parameters should not alter the state

8. Use plural nouns

Do not mix up singular and plural nouns. Keep it simple and use only plural nouns for all resources.

/cars instead of /car

/users instead of /user

/products instead of /product

/settings instead of /setting

9. Use sub-resources for relations

If a resource is related to another resource use subresources.

GET /cars/711/drivers/ Returns a list of drivers for car 711

GET /cars/711/drivers/4 Returns driver #4 for car 711

10. 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-Header.

Content-Type defines the request format.

Accept defines a list of acceptable response formats.

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

Filtering:

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

GET /cars?color=red Returns a list of red cars

GET /cars?seats<=2 Returns a list of cars with a maximum of 2 seats

Sorting:

Allow ascending and descending sorting over multiple fields.

GET /cars?sort=-manufactorer,+model

This returns a list of cars sorted by descending manufacturers and ascending models.

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 /cars?fields=manufacturer,model,id,color

Paging

Use limit and offset. It is flexible for the user and common in leading databases. The default should be limit=20 and offset=0

GET /cars?offset=10&limit=5

To send the total entries back to the user use the custom HTTP header: X-Total-Count.

12. URI case

When it comes to naming resources in a program, there are 3 main types of case conventions: CamelCase, snake_case, and spinal-case. They are just a way of naming the resources to resemble natural language while avoiding spaces, apostrophes, and other exotic characters. This habit is universal in programming languages where only a finite set of characters is authorized for names.

CamelCase

CamelCase has been popularized by the Java language. It intends to emphasize the beginning of each word by making the first letter uppercase. E.g. camelCase, currentUser, etc. Aside from debates about its readability, its main drawback is to be ineffective in contexts which are not case sensitive.

snake_case

snakecase has been widely used for years by C programmers, and more recently in Ruby. Words are separated by underscores “”, thus letting a compiler or an interpreter understand it as a single symbol, but also allowing readers to separate words fluently. However, its popularity has decreased due to a lot of abuses in C programs with over-extended or too short names. Unlike camel case, there are very few contexts where snake case is not usable. Examples: snakecase, currentuser, etc.

spinal-case

spinal-case is a variant of snake case which uses hyphens “-” to separate words. The pros and cons are quite similar to those of snake case, with the exception that some languages do not allow hyphens in symbol names (for variable, class, or function naming). You may find it referred to as lisp-case because it is the usual way to name variables and functions in lisp dialects. It is also the traditional way of naming folders and files in UNIX and Linux systems. Examples: spinal-case, current-user, etc.

According to RFC3986, URLs are “case sensitive” (except for the scheme and the host).

In practice, though, a sensitive case may create dysfunctions with APIs hosted on a Windows system.

It is recommended to use spinal-case (which is highlighted by RFC3986), this case is used by Google, PayPal and other big companies.

13. HTTP Methods

One of the key objectives of the REST approach is using HTTP as an application protocol in order to avoid shaping a homemade API. Hence, we should systematically use HTTP verbs to describe what actions are performed on the resources and facilitate the developer’s work handling recurrent CRUD operations.

The following methods are usually observed:

GET

The GET method is used to retrieve information from the given server using a given URI. Requests using GET should only retrieve data and should have no other effect on the data.

HEAD

Same as GET but transfers the status line and header section only.

POST

A POST request is used to send data to the server, for example, customer information, file upload, etc. using HTML forms.

PUT

Replaces all current representations of the target resource with the uploaded content.

DELETE

Removes all current representations of the target resource given by a URI.

OPTIONS

Describes the communication options for the target resource.

14. API Gateway (Cloud Products)

The uniform API Gateway (e.g. AWS AI Gateway or internal API Gateway) would be helpful in functionality evolution around rate-limit, burst-rate, quota-limit, threshold action, etc.

15. IAM authorization (Cloud Products)

The uniform IAM (e.g. AWS IAM or internal IAM) would be helpful in funationality evolution

16. SSL everywhere (with TLS 1.2 and higher)

Web Linking with REST

  • A link provides a means of navigation from one resource to another.

  • Worldwide Web uses hyperlink to traverse the resource

References