2

When implementing GET on a resource is makes sense to respond with 404 if the resource cannot be found. For POST and PUT verbs it is a little more complicated.

To respond with 404 in that case I would need to do some kind of pre flight check. First do a SELECT in the database, check if there is a matching row, if not, respond with 404. But due to concurrent users there is no guarantee that the following UPDATE sql command will affect any rows.

Is it good practice to implement a pre flight check for resources in a REST API even if there is no guarantee that the request will be processed successfully anyway?

5
  • transactions are a thing
    – Ewan
    Feb 10, 2020 at 14:21
  • But how does that help in this case? Can I expect the exception from a failed transaction to tell me whether it failed because of a concurrently deleted or modified row? 404 is only appropriate in the former. Feb 10, 2020 at 14:40
  • HTTPS error codes don't have to be precisely correct. They're already not a comprehensive list of things that can go wrong anyway. They're certainly not precise enough to tell you why a page was not found. Feb 10, 2020 at 14:43
  • 2
    And what @Ewan is pointing out (more or less) is that the entire transaction succeeds or the entire transaction fails. Once again, the HTTP Response Code doesn't have anything to say about why. Feb 10, 2020 at 14:46
  • "Can I expect the exception from a failed transaction to tell me whether it failed because of a concurrently deleted or modified row?" You might want to consider 409 Conflict if that's a concern.
    – JimmyJames
    Feb 10, 2020 at 22:04

2 Answers 2

3

Ultimately, I think your question has roots in the common misuse of HTTP verbs.

PUT

It's common to equate a PUT to an UPDATE in an RDBMs but it's actually more like an 'UPSERT' (i.e. update or insert if not present).

So as pointed out by Robert Harvey in comments on another answer, at first glance, it doesn't make sense to return a 404 not found in the case that you understand what kind of resource is being updated (or it's parent). Consider the following URI:

https://domain.com/{root}/{parent}/{child}

So given the above it makes sense to return a 404 if you try to PUT against a root or parent that don't exist and if these are fixed, then that's likely what a decent framework will do. If child doesn't exist, then we can create it. However, there are some caveats.

Allow me an aside: Suppose you do allow clients to create resources with PUT as described above. But lets suppose multiple users get to create off of the same root/parent path. What should happen if one user tried to PUT to a resource created by another (unaffiliated) user? There are two possibilities here: they have made an error such as trying to create an new resource or they are purposely trying to do something bad. In either case, you don't want to allow that so you can return a 403 (forbidden) error. But you are also allowed to return a 404 in the case that you don't want to give away info about whether that path exists. But if you only return a 201 when a path doesn't exist, you again leak information. This suggests that 404 could be used in this kind of scenario. It also brings into question whether such a scheme is a good idea. If you make a user/client identifier part of the path, you can avoid such accidental collision in the first place. I won't say it never makes sense to go about things this way but it does potentially make it harder to distinguish between error scenarios on the client.

But what if I don't want to let users create their own resources with PUT?

No problem: you don't have to. Just because the spec says PUT can create or update doesn't mean you are forced to create any URI they send on a PUT. It should be apparent that you can create rules around what URIs can be created with PUT against a path and one valid rule is 'none'. Some error codes that might work for that are 400 or 422. And in the scenario above I made the argument that a 403 or 404 might also make sense. Personally, I would avoid 404 here and use a 'bad input' response to make it clear that you aren't allowing creation of resources with PUT.

POST

There's also a bit of confusion around POST. While it's somewhat aligned with the concept of an INSERT, there's one aspect that I think a lot of people miss: the path in a POST request doesn't refer to the resource being created. It's the path of the parent resource. That is when you POST to

https://domain.com/{root}/{parent}

you are asking to create a new resource

https://domain.com/{root}/{parent}/{child}

and the identifier child is created by the server (although nothing says you can't have the client specify that in the content of the request.)

So in the context of your question, it doesn't make sense to return a 404 when the child doesn't exist because you are asking the server to create it and it shouldn't be part of the URI anyway. However, if the parent resource doesn't exist, it makes perfect sense to return a 404 in that case as the URI would be asking to create a child of a non-existent resource.

To conclude, I would suggest avoiding the use of 404 with PUT and POST to indicate the lack of a child entity under here because it a little confusing at best and potentially just plain wrong. It does make sense to use 404 to indicate that the parent path is non-existent or not accessible by the client.

1
  • "it doesn't make sense to return a 404 when the child doesn't exist because you are asking the server to create it and it shouldn't be part of the URI anyway. However, if the parent resource doesn't exist, it makes perfect sense to return a 404" very good comment" that makes a lot of sense. As a side note I have been debating whether PUT is worth implementing at all and just let my POST handler differentiate between Id property being set in body or not. I realize PATCH may be more appropriate in some cases but my framework does not support it ¯_(ツ)_/¯ Feb 11, 2020 at 9:30
5

I feel safe in saying that there is a large consensus amongst experts against using 404 to indicate that the object your API is looking for in its database was not found.

The main issue is that you will also get 404 when your webserver or client is misconfigured, or you simply mistype the domain.

The second is that you often don't want to throw an exception when a resource is not found, but html libraries will often throw exceptions on receipt of a 404 code.

However, If you want to use 404 for this purpose the implementation is simple enough even for an "attempted update against non existing resource".

  • Begin a transaction
  • Check for existence of the resource
  • If its not there, return 404 and roll back transaction
  • If it is there, continue with update and commit transaction

There is also the perhaps more optimal 'run sql and check number of rows affected' but the result is more open to interpretation.

Alternatives to 404 (note this part is only for those who want an explanation of the first sentence)

Basically it comes down to the design of your code. You want to avoid forcing the user to check whether the method they just called succeeded or not. This allows them to write clear, simple code.

So. Exceptions when you can't recover, but more importantly, methods designed never to throw exceptions.

So if I'm trying to load objects that might not exist, Help me out with a method which recognised that "that doesn't exist" is a valid result of the call, not an exceptional circumstance.

If I'm trying to update an Object, think about how the system should handle it being deleted while I've had it open. Maybe you can just create object that don't already exist, or maybe updating sets where the is no match is fine.

In code a get function might return a null, an update might return number of rows affected. These can be represented as "null" or 0 rather than an error code.

29
  • 5
    Can you provide sources for your claim that there is a large consensus amongst experts against using 404 for this situation? Feb 10, 2020 at 16:21
  • 5
    I wouldn't take the upvotes as agreement as it is unrelated to the question and you do answer it quite well. That being said, my experience is actually the complete opposite of the "large consensus", both in my actual field of work and on most articles or blogs I have read. I also can't say your arguments convinced me, but I'm willing to stay open if you have any other sources that could enlighten me. Feb 10, 2020 at 16:28
  • 4
    To add to the pedantry: technically a PUT should never return 404. In the absence of an existing resource, the correct behavior is to create a new resource and return 201. See developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT Feb 10, 2020 at 16:57
  • 1
    @JimmyJames: If you want full control over exactly what happens, you can always use a POST. Feb 10, 2020 at 18:05
  • 1
    @Ewan "POST and 200/500. so you never have to have this discussion again" Right because that worked so well with the SOAP/WSDL debacle. 500 indicates a server error. When you return that, you are indicating you have bugs and/or misconfiguration on the server.
    – JimmyJames
    Feb 10, 2020 at 22:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.