avatarPavle Djuric

Summary

The web content provides an in-depth guide on REST API best practices, focusing on the structure of HTTP requests, including the request line, headers, and message body.

Abstract

The article "REST API Best Practices: The Main Elements of an HTTP Request" delves into the critical components of HTTP requests within REST APIs. It emphasizes the importance of using appropriate HTTP methods (GET, POST, PUT, PATCH, DELETE) and the significance of idempotency and safety in method selection. The author advises against using verbs in path parameters, advocating for a resource-based hierarchy that reflects the data model. The use of query parameters for filtering resources is discussed, with examples illustrating how they correspond to database queries. The article also touches on the evolution of the HTTP protocol, the role of headers, particularly the mandatory 'host' header in HTTP/1.1, and the use of custom headers. The message body is highlighted as the payload carrier for requests and responses, with a preference for JSON formatting. The author stresses the need for careful planning in the initial design phase of a REST API to avoid later refactoring difficulties.

Opinions

  • The author expresses a clear preference for using PATCH over PUT for partial updates due to practicality.
  • A hierarchical approach to resource paths is recommended, with a belief that it should mirror the application's data model.
  • The article suggests that using query parameters in non-GET methods is generally an anti-pattern, except in specific cases where it explicitly defines the request's intent.
  • There is a strong endorsement for JSON as the primary format for request/response bodies in REST APIs, with XML mentioned as a less favorable alternative.
  • The author emphasizes the importance of the initial API design phase, cautioning that changes later in the project can be challenging.

REST API Best Practices : The Main Elements of an HTTP Request

Photo by Diane Picchiottino on Unsplash

An HTTP request consists of three main elements:

The request line, the headers and the message body.

In this article I will be examining each of these elements and all of the building blocks that they use. A well designed REST API needs to make proper use of all of these building blocks.

The Request Line

The request line consist of the following elements:

  1. The method describing the action we want the server to perform (GET, POST, PUT, PATCH, DELETE etc.).

When designing your REST API, make sure to use the proper method for each endpoint. It’s relatively easy, since nearly all of your requests will be using one of the five that I have mentioned. GET will be for all queries that need to retrieve data. POST will be for all newly created data. DELETE will be used for deleting data (pretty obvious, right?). As far as PUT/PATCH, the general rule is that you should use PATCH when you are sending only the field(s) being updated, while PUT requests should contain the entire resource, including the fields that are not being updated. The reason for this, is because ideally PUT should try to update an existing resource, however if that resource does not exist, it should create a new one. I personally prefer PATCH for updating, as it is more practical to only pass the data that needs to be updated, however you will most likely encounter PUT requests more.

All of these methods should be idempotent (meaning executing them once will have the same result as executing them infinitely) except POST, and only GET should be a safe method (meaning it does not change the state of the resource).

  1. The resource we wish to manipulate (e.g. /users) also know as path parameters

Path parameters are an essential part of a well designed REST API. There are many issues that need to be addressed before deciding on how to construct the path parameters of your HTTP requests. The first rule that is often ignored is- never use verbs in path parameters, that’s what the methods are there for. Do not do this

GET /get_authors

REST APIs are resource based, unlike RPCs which are action based. You should only include the resource in the path, never the action:

GET /authors

Another issue to consider is the hierarchy of your resources. This depends on the hierarchy of your data model, no matter if you are using SQL or NoSQL.

For example, if your app is for writing blogs (like Medium), your data model will likely consist of a table with users, and a table with articles ( yes, I’m a bigger fan of sql than nosql). So if I wanted to get all articles written by Nassim Taleb I could send 2 kinds of HTTP requests:

GET /articles?author=NassimTaleb

Or

GET /authors/NassimTaleb/articles

I personally prefer the second approach, because the request describes the resource hierarchy. Authors have articles, not the other way around.

It’s also worth noting that general resource names (authors, articles etc.) should be followed only by some unique identifier ( i.e. the authors username or maybe an id). That way you separate list endpoints from detail endpoints. List endpoints should return a list of all items for that resource, while detail endpoints should return a detailed payload of that specific item for that specific resource (in this case it would be a single article written by Nassim Taleb including it’s full content).

3.The protocol we’re using for communication (HTTP 1.0, HTTP 1.1 etc.)

The first version of the HTTP protocol (0.9) was created in 1990 by Tim Berners Lee. Since then it’s been all uphill for HTTP. Quickly after, 1.0 was released, but it had many flaws (e.g. the host header was not mandatory), so only a couple of months afterwards version 1.1 was released. It is still the most widely used version, even though we now have HTTP/2 , and even HTTP/3 (which supposedly runs over UDP instead of TCP, don’t ask how I have no idea).

4. Query parameters used to filter the resource (e.g ?country=Spain)

They are mostly used for filtering GET requests as we saw in one of the above examples. The main idea is to pass these parameters to your database query (hence the name). Just imagine them as all of the clauses being passed after the WHERE clause in your SQL query. So a request such as:

GET /authors?country=Spain&genre=programming

should ideally translate to the following query to your db server:

SELECT * FROM authors WHERE country=Spain AND genre=programming

You can even pass the specific fields that you want to retrieve in your API response by adding something like this:

GET /authors?country=Spain&genre=programming&fields=name,age

This would then send the following query to your db:

SELECT name,age FROM authors WHERE country=Spain AND genre=programming

In your face GraphQL! :)

Incidentally, you can also pass query parameters to a request that uses a method other than GET, but this is very rare and should be used only when there is an appropriate use case. I have seen POST requests that pass data through query parameters like this:

POST /messages?text=hello&destination=world

But I think this is an anti-pattern and should be avoided. If for no other reason, then for the fact that query params can contain a maximum of 2048 characters.

A good use of a query param in a post request may be if you want to explicitly declare a way something should be executed. Let’s say that the request above allows users to send messages via different protocols. You could pass a protocol field in the message body, but you could also pass this in a query parameter, thus making it more explicit of the intent:

POST /messages?protocol=xmpp

Not something that is seen often, but as Mills Lane says:

HTTP Headers

HTTP headers are used when sending an HTTP request, and when the server sends back the response. They are mostly used for describing meta-data about the request.

There are many HTTP headers that can be passed, including custom headers, however one header is mandatory since HTTP version 1.1 (which is still the most widely used version) - the host header. When you enter a url into your browser, the part right after the protocol declaration, and before the first trailing slash, gets automatically inserted into the host header.

Other notable headers include content-type, authorization, content-length and many more. Although the authorization header key is most commonly used for authenticating REST APIs, you are free to implement a custom header for this. Many REST APIs use something like x-api-key if their APIs are protected by an auth key.

Custom headers usually use a x prefix. So when you see x-forwarded-for or x-forwarded-port those are custom headers added by the reverse proxy that is sitting in between your application server and the client (usually NGINX).

All custom headers added by AWS when using REST API to call their services start with the x-amz prefix. So just remember when adding custom headers to include the x prefix.

Message Body

The message body is the actual payload of the request or response. Message bodies should only be used for certain types of requests (POST, PUT, PATCH). So technically, even though you can send a request body for a GET request, it should not have any meaning and you should never program your application to read the body of a GET request.

As for server responses, the body should always contain some kind of data, except for when the method is DELETE. In that case, if the request has been processed successfully, it is encouraged to send an empty response along with a 204 status code, indicating that the data has successfully been deleted on the server side.

For REST APIs, the most common format of a request/response body is JSON. You should definitely stick to this. Occasionally, XML can be an additional option, although is has been losing the battle for very long. If you do decide to include XML as an option, the request should contain a format query parameter to indicate that the client is expecting a response in XML:

GET /authors?format=xml

You could theoretically pass other formats like plain text or html, but I haven’t seen this implemented anywhere.

These essential building blocks must be carefully considered and handled with care. Do not rush the initial design of your REST API. Carefully chose which parameters you will use for query parameters, which for path parameters, and how your custom headers will look like. Refactoring these can be a real pain once the project has advanced, so make sure to think these factors through at the beginning.

That’s all for this article. Thank you very much for reading :)

Http Request
Rest Api
Software Development
Web Development
Http Client
Recommended from ReadMedium