haselkern

API Errors Are Not Protocol Errors

I visit the programming subreddit from time to time to check for any projects that might be worth looking into, when I stumbled on something interesting. People were making fun of APIs that returned a status code 404 (NOT FOUND) inside the response, while having a HTTP status 200 (OK).

$ curlie api.example/some/resource
HTTP/1.1 200 OK
content-type: application/json

{
    "status": 404,
    "data": null
}

(I like using curlie because it shows more information out of the box like I would expect curl to work, including some highlighting.)

It was argued that in this case the API should just return HTTP status 404, without any content, because having multiple status codes is just silly. I would disagree, but lets look at an example first. Say I can get details about some product by its ID from an API:

$ curlie api.example/producs/123
HTTP/1.1 404 Not Found

Hm, there seems to be no product with ID 123. Oh wait, I just made a typo, it's obviously products, not producs.

$ curlie api.example/products/123
HTTP/1.1 404 Not Found

So, uh, there is no product with ID 123? I think. Or maybe the endpoint does not exist. And that's a problem you cannot really solve by cramming results from your API into an HTTP status code, which is why I think that API errors should not be treated as protocol errors.

There are two conceptual layers at play here, the HTTP layer that transports some bytes with metadata from A to B and your application on top that wants to transport structured information using the layer below. Confusing status codes from one layer with those from the other one is a burden for everyone involved. The initial example I gave was actually pretty close to a response I would expect from a nice API server, but using status 404 has no place in application data, were such an HTTP specific code is not relevant. Something like this would probably be more readable:

$ curlie api.example/products/123
HTTP/1.1 200 OK
content-type: application/json

{
    "error": {
        "code": "not_found",
        "message": "The requested product could not be found."
    },
    "data": null
}

Notice how the HTTP status code is 200 because transport was okay, but the response contains an application specific error. This is also the approach GraphQL has taken, were servers usually only have a single endpoint you can post to. This endpoint always returns HTTP 200 if the server works, with relevant data being encoded in the request and response data.


This post is just my personal opinion, if you like using HTTP status codes in your API that's fine! But maybe reading this gave you some things to think about when designing or working with your next API.

Tags: #programming #web