REST API 404: Bad URI, or Missing Resource?

That is an very old post but I faced to a similar problem and I would like to share my experience with you guys.

I am building microservice architecture with rest APIs. I have some rest GET services, they collect data from back-end system based on the request parameters.

I followed the rest API design documents and I sent back HTTP 404 with a perfect JSON error message to client when there was no data which align to the query conditions (for example zero record was selected).

When there was no data to sent back to the client I prepared an perfect JSON message with internal error code, etc. to inform the client about the reason of the "Not Found" and it was sent back to the client with HTTP 404. That works fine.

Later I have created a rest API client class which is an easy helper to hide the HTTP communication related code and I used this helper all the time when I called my rest APIs from my code.

BUT I needed to write confusing extra code just because HTTP 404 had two different functions:

  • the real HTTP 404 when the rest API is not available in the given url, it is thrown by the application server or web-server where the rest API application runs
  • client get back HTTP 404 as well when there is no data in database based on the where condition of the query.

Important: My rest API error handler catches all the exceptions appears in the back-end service which means in case of any error my rest API always returns with a perfect JSON message with the message details.

This is the 1st version of my client helper method which handles the two different HTTP 404 response:

public static String getSomething(final String uuid) {
    String serviceUrl = getServiceUrl();
    String path = "user/" + , uuid);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_UTF8)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        // HTTP 200
        return response.readEntity(String.class);
    } else {
        // confusing code comes here just because
        // I need to decide the type of HTTP 404...

        // trying to parse response body
        try {
            String responseBody = response.readEntity(String.class);
            ObjectMapper mapper = new ObjectMapper();
            ErrorInfo errorInfo = mapper.readValue(responseBody, ErrorInfo.class);

            // re-throw the original exception
            throw new MyException(errorInfo);

        } catch (IOException e) {
            // this is a real HTTP 404
            throw new ServiceUnavailableError(response, requestUrl, httpMethod);
        }

    // this exception will never be thrown
    throw new Exception("UNEXPECTED ERRORS, BETTER IF YOU DO NOT SEE IT IN THE LOG");
}

BUT, because my Java or JavaScript client can receive two kind of HTTP 404 somehow I need to check the body of the response in case of HTTP 404. If I can parse the response body then I am sure I got back a response where there was no data to send back to the client.

If I am not able to parse the response that means I got back a real HTTP 404 from the web server (not from the rest API application).

It is so confusing and the client application always needs to do extra parsing to check the real reason of HTTP 404.

Honestly I do not like this solution. It is confusing, needs to add extra bullshit code to clients all the time.

So instead of using HTTP 404 in this two different scenarios I decided that I will do the following:

  • I am not using HTTP 404 as a response HTTP code in my rest application anymore.
  • I am going to use HTTP 204 (No Content) instead of HTTP 404.

In that case client code can be more elegant:

public static String getString(final String processId, final String key) {
    String serviceUrl = getServiceUrl();
    String path = String.format("key/%s", key);
    String requestUrl = serviceUrl + path;
    String httpMethod = "GET";

    log(requestUrl);

    Response response = client
            .target(serviceUrl)
            .path(path)
            .request(ExtendedMediaType.APPLICATION_JSON_UTF8)
            .header(CustomHttpHeader.PROCESS_ID, processId)
            .get();

    if (response.getStatus() == Response.Status.OK.getStatusCode()) {
        return response.readEntity(String.class);
    } else {
        String body = response.readEntity(String.class);
        ObjectMapper mapper = new ObjectMapper();
        ErrorInfo errorInfo = mapper.readValue(body, ErrorInfo.class);
        throw new MyException(errorInfo);
    }

    throw new AnyServerError(response, requestUrl, httpMethod);
}

I think this handles that issue better.

If you have any better solution please share it with us.


As with most things, "it depends". But to me, your practice is not bad and is not going against the HTTP spec per se. However, let's clear some things up.

First, URI's should be opaque. Even if they're not opaque to people, they are opaque to machines. In other words, the difference between http://mywebsite/api/user/13, http://mywebsite/restapi/user/13 is the same as the difference between http://mywebsite/api/user/13 and http://mywebsite/api/user/14 i.e. not the same is not the same period. So a 404 would be completely appropriate for http://mywebsite/api/user/14 (if there is no such user) but not necessarily the only appropriate response.

You could also return an empty 200 response or more explicitly a 204 (No Content) response. This would convey something else to the client. It would imply that the resource identified by http://mywebsite/api/user/14 has no content or is essentially nothing. It does mean that there is such a resource. However, it does not necessarily mean that you are claiming there is some user persisted in a data store with id 14. That's your private concern, not the concern of the client making the request. So, if it makes sense to model your resources that way, go ahead.

There are some security implications to giving your clients information that would make it easier for them to guess legitimate URI's. Returning a 200 on misses instead of a 404 may give the client a clue that at least the http://mywebsite/api/user part is correct. A malicious client could just keep trying different integers. But to me, a malicious client would be able to guess the http://mywebsite/api/user part anyway. A better remedy would be to use UUID's. i.e. http://mywebsite/api/user/3dd5b770-79ea-11e1-b0c4-0800200c9a66 is better than http://mywebsite/api/user/14. Doing that, you could use your technique of returning 200's without giving much away.


404 is just the HTTP response code. On top of that, you can provide a response body and/or other headers with a more meaningful error message that developers will see.


Use 404 if the resource does not exist. Don't return 200 with an empty body.

This is akin to undefined vs empty string (e.g. "") in programming. While very similar, there is definitely a difference.

404 means that nothing exists at that URI (like an undefined variable in programming). Returning 200 with an empty body means that something does exist there and that something is just empty right now (like an empty string in programming).

404 doesn't mean it was a "bad URI". There are special HTTP codes that are intended for URI errors (e.g. 414 Request-URI Too Long).