Recently I've been working on an API based system, which used HTTP as protocol for communication. It's nothing really unusual, everyone use it, because it has a lot of beneficial features and it's quite mature.
Yet when applying HTTP for purpose of API calls in frontend-backend or backend-backend calls you face some fundamental (well, not so fundamental in the live-death sense, but still) questions.
What was the problem?
In my case the state of the server was involved. If server has a lot of logic to handle, then it's internal state may change over the time. It's worth mentioning that the state is dependent on a business logic, so we are not checking if it's up and running, resources or performance. If the server is distributing tickets for a horse racing event, then we are checking if there is enough places on the tribune, we can finalize the payment, etc.
Let's say we want to POST a new ticket order, but we first need to validate if the server's state is correct. The normal HTTP way to do this would be:
- 1) Client: Make a POST request on a some /order_ticket/ endpoint
- 2) Server:
- - if the state is correct, then return 201 Created
- - if it's not correct (let's say the race was cancelled due to bad weather), return some 4xx or 5xx status code
But wait, which status should I choose?
4xx codes group is generally designated for client errors.
Let's see the formal definition from IETF, which provides rules for HTTP: RFC 7231
The 4xx class of status code is intended for cases in which the client seems to have erred. Except when responding to a HEAD request, the server SHOULD include an entity containing an explanation of the error situation, and whether it is a temporary or permanent condition. These status codes are applicable to any request method. User agents SHOULD display any included entity to the user.
If we want to order ticked for wrong day, we get 400 Bad Request with "Sorry, you cannot order a ticket for that day, because there is no race then.". The same goes for more formal errors like missing data "You should specify your first and last name".
Should we use one of 4xx error? After all, it's not the client's fault that the weather is bad.
Then maybe 5xx? It seems reasonable to blame the server's state for the situation, because it could not fulfill its duty.
The 5xx Server Error
The 5xx (Server Error) class of status code indicates that the server
is aware that it has erred or is incapable of performing the
requested method. Except when responding to a HEAD request, the
server SHOULD send a representation containing an explanation of the
error situation, and whether it is a temporary or permanent
Seems perfectly legit. However, on second thought I had some doubts. 500 error message doesn't really fit:
500 Internal Server Error
The 500 (Internal Server Error) status code indicates that the server
encountered an unexpected condition that prevented it from fulfilling
Our error wasn't totally unexpected, because we were checking if the horse race event was stll to be held. So in fact server didn't fail. In my developer practice (in interpreted languages like Python) I used to treat the 500's as of the total catastrophe, when there is a bug in the code, like... invalid syntax, failed dependency, a component down or another server total trainwreck.
So the 500 is reserved for cases of the developers or maintainers failure. In my opinion, one should never emit 500 error from the application on purpose. This error will be emitted anyway by the software you use, the http server in our case.
So, what else is suitable?
Two response codes were catching my eye:
412 Precondition Failed
The server does not meet one of the preconditions that the requester put on the request.
422 Unprocessable Entity
The 422 (Unprocessable Entity) status code means the server understands
the content type of the request entity (hence a 415 (Unsupported Media Type)
status code is inappropriate), and the syntax of the request entity
is correct (thus a 400 (Bad Request) status code is inappropriate) but
was unable to process the contained instructions. For example, this error
condition may occur if an XML request body contains well-formed
(i.e., syntactically correct), but semantically erroneous XML instructions.
424 Failed Dependency
The 424 (Failed Dependency) status code means that the method could not be
performed on the resource because the requested action depended on another
action and that action failed. For example, if a command in a PROPPATCH
method fails then, at minimum, the rest of the commands will also fail with
424 (Failed Dependency).
All of them were from 4xx group, because I decided that business logic should be handled without 5xx errors, which I found more technical. Well, I cannot strengthen this argument, because it's a matter of convention and it's not worthy a elongated discussion (just like why vim is better than emacs or opposite).
The status code I have chosen was
424 Failed Dependency
Because the server could not carry out its duty due to completely external reason. Thus, no one were to be blamed: not client, nor server.