API Design - choosing status code for a GET route
4 min read
You need to design some routes with the following properties:
- you need to do some computation in the backend
- they are idempotent: every time you call the route with the same params it should give you the same result and nothing should change in the state o the application. Eg. no records are created, updated, deleted ...
- they return a collection of data as response
An endpoint that will for a book returns similar books according to a predefined formula.
An endpoint that will first select some resources and filter them based on some criteria
Or an endpoint that accepts various filtering parameters and returns a collection of resources
In all these cases the main question is:
What should the response be in case there are no resources found?
or more general
How to choose what status to return?
Choosing the HTTP verb
First, I should start by saying that the HTTP verb to be used for such routes is GET.
For me, it feels like the normal choice, here is why:
- The other possible verb could be POST, but POST is mostly used for situations where the request will change the application state (eg: will create a new resource ...)
- JSON:API spec also says:
Data, including resources and relationships, can be fetched by sending a GET request to an endpoint.
Status to return
Choosing a status is dependent on what operation one is doing there. I think it is better to explain this by taking one of the examples above:
In this case, response status should be:
200(ok) if any similar book is found
200(ok) with an empty array if no similar book is found
401(not authorized) is the endpoint that requires authorization it was not provided or invalid
422(unprocessable entity) if the route accepts some query params and they are not valid
404(not_found) if book id does not exist or is not allowed to be queried
I think from all the status responses, one that is worth discussing a little bit is why return
200 (ok) when no results are found and why not return for example
404 (not found)?
The reasons for choosing 200 are:
First, the JSON:API spec defines that in case of fetching a collection of resources the response should be 200, and in case of empty should just include an empty array/data:
A server MUST respond to a successful request to fetch a resource collection with an array of resource objects or an empty array () as the response document’s primary data.
Second, thinking about this the request is valid (give me similar books with this book), and also the empty response is a valid response (there are no similar books) and not an error response. An error response will be like saying "before querying this endpoint you should know if a book has or has not similar books" but this is exactly the purpose of this endpoint to answer this question. So asking the question and answering with the empty array is a valid response for this question.
This, let's try to analyze this status response through the difference between this route and one like
books/:id. In the case of
books/:id if there is no book with the provided id it is expected to return 404 not only because the JSON:API spec is describing very clearly this situation but also because if the queries id does not exist it is like the URL
books/:id does not exist so it is normal to respond with 404. But if the case of
books/:id/similar if there are no similar books then the URL is still valid in the sense that it is still valid to ask the question "Are there any similar books for this specific book id?"
When choosing a status you should also make sure that by choosing that status you will not reveal a piece of information that you did not intend.
Here are is an example of what NOT to do:
409 (conflict) when someone tries to GET /books/:id/similar for a book that exists but is not yet published or active. It could be tempting to respond with
409 as somehow the query is valid but the state of the book is not allowing this request to be processed by doing so you will communicate also that that book id exists in your system even if the API user cannot access it right now. This is a piece of extra information that could allow an attacker to understand what books are not yet published for example.
Did you find this article valuable?
Support Lucian Ghinda by becoming a sponsor. Any amount is appreciated!