94

Let's say I have a simple (Jersey) REST resource as follows:

@Path("/foos")
public class MyRestlet extends BaseRestlet
{        
    @GET
    @Path("/{fooId}")
    @Produces(MediaType.APPLICATION_XML)
    public Response getFoo(@PathParam("fooId") final String fooId)
        throws IOException, ParseException
    {
        final Foo foo = fooService.getFoo(fooId);
    
        if (foo != null)
        {
            return response.status(Response.Status.OK).entity(foo).build();
        }
        else
        {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }
}

Based on the code above, is it correct to return a NOT_FOUND status (404), or should I be returning 204, or some other more appropriate code?

Kit
  • 20,354
  • 4
  • 60
  • 103
carlspring
  • 31,231
  • 29
  • 115
  • 197
  • 1
    If you would expect a resource to be there, because it is looked up using an ID, then a 404 is expected. Something goes wrong, the resource you need to be there is not found. If it would be an endpoint to fetch the list of products that expired yesterday (for instance) but there are none, then a 404 would not be correct since you cannot expect to always have products expire every day. So a 200 with an empty array would probably be better. – Mike de Klerk Nov 25 '20 at 06:20
  • 1
    I personally prefer the 204 - in the past, I actually did 200 with an object data:null, success: false, message: some message and the front end would always get back the same object but with flags on whether something happened or not (with a message). But Im here to find out the best practise. – Andy Jan 26 '22 at 14:31

4 Answers4

111

A 404 response in this case is pretty typical and easy for API users to consume.

One problem is that it is difficult for a client to tell if they got a 404 due to the particular entity not being found, or due to a structural problem in the URI. In your example, /foos/5 might return 404 because the foo with id=5 does not exist. However, /food/1 would return 404 even if foo with id=1 exists (because foos is misspelled). In other words, 404 means either a badly constructed URI or a reference to a non-existent resource.

Another problem arises when you have a URI that references multiple resources. With a simple 404 response, the client has no idea which of the referenced resources was not found.

Both of these problems can be partially mitigated by returning additional information in the response body to let the caller know exactly what was not found.

Rob
  • 6,247
  • 2
  • 25
  • 33
  • 6
    I would like to add that in the case of `/food/1` returning 404 because that url doesn't exist, it is a client error (he entered the wrong url) and thus not your problem – Tim Nov 10 '14 at 14:48
  • 64
    @TimCastelijns I agree it is the client's error. However, if the client is your customer then it becomes your problem (they are going to pick up the phone and call someone). If we can help the clients use the API without experiencing lots of frustration, we all win. – Rob Nov 10 '14 at 14:58
  • 7
    If you have provided the customer with proper API documentation, it's certainly their fault ;-) but I get your point – Tim Nov 10 '14 at 15:01
  • 9
    I would favor 204 in this case instead of 404 for a few reasons. For ops pple usually 404 rings a bell for further investigation and in many use cases it's just that a resource was not yet created hence no content yet, like it many times is also used(204) as a result of a successfully DELETE call in the resource. Also 204 clearly indicates that no body will be returned which is very clear for the caller(client) that needs to consider an empty response. For browsers many times 404 shows in red which resembles an error and is shown in red as such. – groo Sep 15 '18 at 11:10
  • 13
    IMHO, you should never raise a 404 error when the API endpoint is supposed to return a collection of resources. If no result is found, the result should simply be an empty list. That's the most natural way of interpreting such result. Imagine if Google (or any search engine for that matter) were showing a 404 error every time your search didn't yield any result. So the 404 error is more appropriate when requesting an individual resource that has a URI or something similar. – asiby Feb 18 '20 at 18:56
  • 4
    @asiby The context of this question and answer are an endpoint that returns a particular resource, not a collection. If the context were an endpoint that returns a collection, then I would agree that an empty collection should be returned. – Rob Feb 19 '20 at 16:36
  • 1
    @Rob, that's what I said in the last sentence of my comment. If the OP had doubts about 404ing something or not, I just pointed out when it would be appropriate and when it would not. No harm done here. Just sharing some knowledge. – asiby Feb 20 '20 at 14:15
  • Working a lot with microservices, and 404 may mean 2 things - target entity not found, or target microservice not available, and container return 404 instead of application. Especially bad when you read logs, because you may never know what was "real 404 reason" – degr Sep 06 '21 at 10:49
  • I dont understand why this is such a problem.. I'm not a backend dev... but in my mind... 200 the request is successful.. 404 endpoint not found... not having an item should return 200 with a package explaining what happen with an internal code error not an http error... Just like... finding an item returns 200 with a package with information on that item Just like 500 refers to internal error... you dont just return an invalid item in the package with 500 code the context of HTTP errors for me are "connection" related not internal system related – João Serra Sep 26 '22 at 14:18
35

Yes, it is pretty common to return 404 for a resource not being found. Just like a web page, when it's not found, you get a 404. It's not just REST, but an HTTP standard.

Every resource should have a URL location. URLs don't need to be static, they can be templated. So it's possible for the actual requested URL to not have a resource. It is the server's duty to break down the URL from the template to look for the resource. If they resource doesn't exist, then it's "Not Found"

Here's from the HTTP 1.1 spec

404 Not Found

The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent. The 410 (Gone) status code SHOULD be used if the server knows, through some internally configurable mechanism, that an old resource is permanently unavailable and has no forwarding address. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable.


Here's for 204

204 No Content

The server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation. The response MAY include new or updated metainformation in the form of entity-headers, which if present SHOULD be associated with the requested variant.

If the client is a user agent, it SHOULD NOT change its document view from that which caused the request to be sent. This response is primarily intended to allow input for actions to take place without causing a change to the user agent's active document view, although any new or updated metainformation SHOULD be applied to the document currently in the user agent's active view.

The 204 response MUST NOT include a message-body, and thus is always terminated by the first empty line after the header fields.

Normally 204 would be used when a representation has been updated or created and there's no need to send an response body back. In the case of a POST, you could send back just the Location of the newly created resource. Something like

@POST
@Path("/something")
@Consumes(...)
public Response createBuzz(Domain domain, @Context UriInfo uriInfo) {
    int domainId = // create domain and get created id
    UriBuilder builder = uriInfo.getAbsolutePathBuilder();
    builder.path(Integer.toString(domainId));  // concatenate the id.
    return Response.created(builder.build()).build();
}

The created(URI) will send back the response with the newly created URI in the Location header.


Adding to the first part. You just need to keep in mind that every request from a client is a request to access a resource, whether it's just to GET it, or update with PUT. And a resource can be anything on the server. If the resource doesn't exist, then a general response would be to tell the client we can't find that resource.

To expand on your example. Let's say FooService accsses the DB. Each row in the database can be considered a resource. And each of those rows (resources) has a unique URL, like foo/db/1 might locate a row with a primary key 1. If the id can't be found, then that resource is "Not Found"

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • 1
    Thanks for your answer! It's great as it's quite extensive. Both answers were very helpful, but, unfortunately, I can only accept one... :( – carlspring Nov 10 '14 at 14:49
  • 1
    what about a 400? – Belun May 10 '17 at 16:25
  • @Belun Why 400? – Paul Samsotha May 10 '17 at 16:37
  • 1
    url is bad => request is bad (400) – Belun May 10 '17 at 16:44
  • @Belun Doesn't make sense. A URL represents the location of a resource. If the resource for the URL is not found, then the appropriate status is 404 "Not Found" – Paul Samsotha May 10 '17 at 16:46
  • i would consider the url to be made of resource-signature + resource-id. resource-id is bad : very specific error 404. resource-signature is bad : url is bad, more generic error 400 – Belun May 10 '17 at 16:48
  • @Belun If you want to make up your own convention, go for it. But 404 is what is standard and is what everyone knows and expects. – Paul Samsotha May 10 '17 at 16:57
  • @peeskillet no, it is fine. i wanted to understand the convention, not make my own – Belun May 10 '17 at 16:59
  • In the case of POST for creating something, a response of 201 (not 204) can be returned, with the location in header if needed. The newly created object can be in the response body. https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201 – xjlin0 Aug 26 '22 at 14:16
  • I think that using a 404 response for a db row not found is wrong. I'll base on 2 definitions: 1. HTTP 4xx - https://www.rfc-editor.org/rfc/rfc9110.html#section-15.5 states that HTTP in the 400 is a client error. If your API does a `select * from db.table` to display an empty table, does it mean that the client has made an error by viewing the empty content of a table? 2. What is a target resource? - https://www.rfc-editor.org/rfc/rfc9110.html#target.resource Is defined by URI connect or options, aka how your URI is structure to reach its destination its associated options. – DFIVE May 26 '23 at 14:10
  • @DFIVE REST is different. REST is built on top of these HTTP specs, so the meanings may differ a bit. REST resources are identified by the URI. In your example, the db row _is_ the resource. How ever you return it back, is the _representation_ of that source. There is something part of that URI that identifies that source, for example and ID that maps to the ID in the database. In this case the ID not found in the DB, hence the 404 – Paul Samsotha Jun 04 '23 at 01:29
4

Though this question already have an accepted answer, I believe it's really an opinionated thing. Adding my two cents to help you make a more informed decision about the response code.

404 - Not Found. (Reference)

The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.

The resource may exist and you may not have permission to see the resource, will also be equivalent of Not Found. So 404 for a call where data doesn't exist is a very apt thing to do.

Now as for a non-existing URL; though 404 is a widely adapted response code 400 is a more appropriate code.

400 - Bad Request (Reference)

The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).

If you put an invalid parameter in the request, what would be the response code? If query param has a typo, what should be response code?

Answer to both is 400.

Most of the file-servers, return 404 for invalid URL because for an invalid URL they try to look for a file, which they can't find on the storage ~= Resource Not Found

Apart from the HTTP Status Code, the response will have some info about the error details, where one can be more descriptive about the error and can clear the ambiguity.

If client is calling with an invalid URL, it's an integration issue and should be caught at least during the sanity. No-way they will push the code to production without testing and catching this. Even if they do, God bless them!

tl;dr - 404 for not-found resource; 400 for not-found URL.

Ashwani Agarwal
  • 1,279
  • 9
  • 30
  • 7
    No permissions to access resource should return a 403 (if authenticated) and 401 ( if authentication is required) not a 404. – Raanan Dec 07 '21 at 12:22
  • @Raanam we still use 404 in this way because we don't want unauthenticated user know whether the resource is existed. – Lu Kuan Tsen Nov 19 '22 at 02:09
  • @LuKuanTsen I'll say it depends on your requirement or how you want your API to behave. It's a subjective/situational thing. In some cases you may want user to know that they do not have permission; hence 403 is apt (e.g I'm sharing a tweet of User A to User B, who is blocked by A). – Ashwani Agarwal Nov 23 '22 at 14:20
  • 2
    Please don't include AI responses as a source. They stitch together answers, and is also decidedly incorrect in this case. – Evert Dec 06 '22 at 21:35
  • 2
    this is completely wrong. 400 is out of context and not even remotely related to the question. – saran3h Mar 29 '23 at 06:18
3

A 4XX error code means error from the client side.
As you request a static resource as an image or a html page, returning a 404 response makes sense as :

The HTTP 404 Not Found client error response code indicates that the server can't find the requested resource. Links which lead to a 404 page are often called broken or dead links, and can be subject to link rot.

As you provide to clients some REST methods, you rely on the HTTP methods but you should not consider REST services as simple resources.
For clients, an error response in the REST method is often handled close to errors of other processings.

For example, to catch errors during REST invocations or somewhere else, clients could use catchError() of RxJS.

We could write a code (in TypeScript/Angular 2 for the sample code) in this way to delegate the error processing to a function :

return this.http
  .get<Foo>("/api/foos")
  .pipe(
      catchError(this.handleError)
  )
  .map(foo => {...})

The problem is that any HTTP error (5XX or 4XXX) will terminate in the catchError() callback.
It may really make the REST API responses misleading for clients.

If we do a parallel with programming language, we could consider 5XX/4XX as exception flow.
Generally, we don't throw an exception only because a data is not found, we throw it as a data is not found and that that data would have been found.
For the REST API, we should follow the same logic.

If the entity may not be found, returning OK in the two cases is perfectly fine :

@GET
@Path("/{fooId}")
@Produces(MediaType.APPLICATION_XML)
public Response getFoo(@PathParam("fooId") final String fooId)
        throws IOException, ParseException {
    final Foo foo = fooService.getFoo(fooId);

    if (foo != null){
        return Response.status(Response.Status.OK).entity(foo).build();
    }

    return Response.status(Response.Status.OK).build();

}

The client could so handle the result according to the result is present or missing.
I don't think that returning 204 brings any useful value.
The HTTP 204 documentation states that :

The client doesn't need to go away from its current page.

But requesting a REST resource and more particularly by a GET method doesn't mean that the client is about terminating a workflow (that makes more sense with POST/PUT methods).

The document adds also :

The common use case is to return 204 as a result of a PUT request, updating a resource, without changing the current content of the page displayed to the user.

We are really not in this case.

Some specific HTTP codes for classical browsing matche finely with return codes of REST API (201, 202, 401, and so for...) but this is not always the case. So for these cases, rather than twisting original codes, I would favor to keep them simple by using more general codes : 200, 400.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • @Downvoter could you please share your thought ? It could be useful. – davidxxx Jul 29 '18 at 11:13
  • 6
    If we're trying to be restful then I think the API should return 404 for a path like `/foos/{fooId}` because in REST that is a URL to a resource that does not exist. Compare this with a `/foos?id=6` where the URL points to the `foos` resource and we're passing a filter. In this case it is ok to return an empty response because `foos` resource does exist and there are 0 entities that match the filter. – daramasala May 28 '19 at 15:16
  • What if we want to update an object which does not exist? There's definitely a need to return an error to the client – Matvey Masov Dec 06 '19 at 15:06
  • @daramasala exactly my thoughts in this 404 vs 200 thingy .. It depends on the endpoint. – Karl Anthony Baluyot Jan 26 '21 at 01:15
  • 2
    @davidxxx I disagree REST resources are not different from _other_ resources. It the cannot be found, they cannot be found. Just like a html file that is not there. It is a 404 – theking2 Mar 14 '23 at 20:14