REST API Best Practices : The Main Elements of an HTTP Request
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:
- 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).
- 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_authorsREST APIs are resource based, unlike RPCs which are action based. You should only include the resource in the path, never the action:
GET /authorsAnother 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=NassimTalebOr
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=programmingshould ideally translate to the following query to your db server:
SELECT * FROM authors WHERE country=Spain AND genre=programmingYou 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,ageThis would then send the following query to your db:
SELECT name,age FROM authors WHERE country=Spain AND genre=programmingIn 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=worldBut 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=xmppNot 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=xmlYou 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 :)






