213

Given that the DELETE verb in HTTP is idempotent, when I issue the following request, what should happen the second (or third, or fourth, etc...) time I make it?

DELETE /person/123

The first time, the resource is deleted and I return a 204 (successful, no content). Should I return a 204 on subsequent calls or a 404 (not found)?

Palec
  • 12,743
  • 8
  • 69
  • 138
Craig Wilson
  • 12,174
  • 3
  • 41
  • 45
  • 2
    A more generic Q&A about whether idempotency includes status codes: https://stackoverflow.com/q/24713945/2157640 Spoiler, it does not. – Palec Sep 17 '21 at 14:07

4 Answers4

245

As HTTP requests in a stateless system should be independent, the results of one request should not be dependent on a previous request. Consider what should happen if two users did a DELETE on the same resource simultaneously. It makes sense for the second request to get a 404. The same should be true if one user makes two requests.

I am guessing that having DELETE return two different responses does not feel idempotent to you. I find it useful to think of idempotent requests as leaving the system in the same state, not necessarily having the same response. So regardless of whether you DELETE an existing resource, or attempt to DELETE a resource that does not exist, the server resource state is the same.

Darrel Miller
  • 139,164
  • 32
  • 194
  • 243
  • 8
    Thank you. That makes so much sense. I was indeed thinking of idempotent as returning the same response. – Craig Wilson Jun 22 '11 at 13:09
  • 4
    @Craig Careful! In the Cookbook, Subbu completely contradicts what I just said. He says idempotency means it should return the same response. Luckily, Subbu is going to be at RESTFest so, I'm going to clarify with him there. – Darrel Miller Jun 22 '11 at 13:15
  • Well, it makes sense to me and is in accordance with what I initially thought in regards to what idempotency meant. Think about the time when user A issues a GET request for item 1, user B issues a POST request that updates item 1, and user A issues a GET request for item 1 again. user A will get different responses. That indicates to me that response has nothing to do with idempotency. – Craig Wilson Jun 22 '11 at 13:42
  • I agree that it doesn't feel idempotent to have 2 different responses for the same request. I feel like a DELETE request is more along the lines of "make sure this resource is deleted". If it is already deleted or deleted by the request doesn't change the result of the request, "OK", the server responds, the resource isn't there. – Jeff Martin Aug 13 '12 at 16:50
  • 123
    If you DELETE something that doesn't exist, you should just return a 204 (even if the resource never existed). The client wanted the resource gone and it is gone. Returning a 404 is exposing internal processing that is unimportant to the client and will result in an unnecessary error condition. – Brian Feb 16 '14 at 07:07
  • 2
    @Brian that is an interesting approach. Personally, I would like some kind of confirmation that I actually used the correct Uri. With a 204 for. Everything I have no idea if the server did what I wanted or not. – Darrel Miller Feb 16 '14 at 13:32
  • 1
    I'm not sure that I follow your argument. The status code doesn't tell you what resource you are deleting (that's in the URI). If you get a 404 response, does that mean you should alter the URI and try again? It seems that a 204 would say that the correct URI was used. – Brian Feb 17 '14 at 00:14
  • 1
    @Brian. You said return 204, even if the resource never existed. If the resource never existed, I think I'd like the client to know that it just tried to delete something that never did exist. Either way, as Julian mentions in his answer, HTTP doesn't care, do what works for you. – Darrel Miller Feb 17 '14 at 01:11
  • 11
    @DarrelMiller I guess the key concept here is that you should not use DELETE to check if a resource exists, you'd first use GET for that. Then, if the response is 200, you'd perform a DELETE; otherwise don't even bother to do that. So I think it makes sense to always return a 204 on DELETE. – manei_cc Jan 29 '15 at 20:09
  • 2
    @manei_cc If you want to check for the existence of a resource a HEAD request might be an even cheaper way to go. – Darrel Miller Jan 29 '15 at 20:36
  • 2
    @DarrelMiller did you ask Subbu back in 2011 ? :) – kiedysktos May 25 '17 at 11:54
  • 1
    If a second attempt returns 404, there's a risk of misbehavior on the client. I added an answer to explain this possibility. So, best is to return 200 (or 204) on subsequent DELETE requests. – Paulo Merson Jul 19 '17 at 16:55
  • 25
    @Brian The RFC says it's supposed to behave like `rm`. `rm` returns an error if it doesn't exist. https://tools.ietf.org/html/rfc7231#section-4.3.5 – Dax Fohl Sep 22 '17 at 22:59
  • 1
    @DarrelMiller - Can we make DELETE return a 204 all the time ? Then, to confirm that your DELETE worked, you try a GET. Is that how one should implement DELETE ? – MasterJoe Nov 01 '17 at 18:48
  • 5
    @DaxFohl Technically, it says, "this method is similar to the rm command," which I interpret as an analogy, rather than a recommendation on behavior. – plantbeard Jan 13 '20 at 21:50
  • 1
    @DaxFohl - I agree completely with plantbeard on this: they invoke rm just as a limited example to explain that DELETE is generally going to remove an association and that it doesn't automatically assume the actual underlying data is removed. If they say anything, it's return a 2xx code, but even that is really vague, so I expect they're deliberately being vague as this really is a higher level topic than the spec should be concerned with. – Jim L Sep 26 '20 at 16:06
  • 2
    I write a lot of client code that relies on server-side optimistic concurrency. Such code, where conditional (If-Match, etc.) GET, PATCH and DELETE are the norm, is certainly simpler when the server responds with a success code to a redundant DELETE. The alternative is somewhat ambiguous since the 404 error on the redundant DELETE could be a client-side error (bad URL) as well as the intended "already gone". While the 404 can be dealt with, doing so opens the door to masking other "real" error conditions. – Lee Dec 27 '20 at 19:37
150

I agree with what the current chosen answer has said, that the 2nd (and 3rd, 4th, ...) DELETE should get a 404. And, I noticed that answer has 143 up votes but also has an opposite comment which has 54 up votes, so the community is divided into 2 camps in roughly 3:1 ratio. Here comes more information to settle this long time debate.

  1. First of all, let's NOT start with what "I" think, what "you" think, or what yet another book author thinks. Let's start with the HTTP specs i.e. RFC 7231.
  • RFC 7231, section 4.3.5 DELETE happened to only mention a successful response should be 2xx, but it did not call out what a subsequent DELETE would get. So let's dig deeper.

  • RFC 7231, section 6.5.4 404 Not Found says 404 response is for a resource does not exist. Since no specific http method (in particular, not DELETE) being called out to be treated otherwise, we can intuitively get an impression (and rightfully so), that my request DELETE /some/resource/which/does/not/exist should result in a 404. Then, DELETE /some/resource/which/happened/to/be/removed/by/someone/else/five/days/ago might as well also return a 404. Then, why should DELETE /some/resource/i/deleted/five/seconds/ago be any different? "But how about idempotency?!", I can hear you are screaming that. Hang on, we are about to get into that.

  • Historically, RFC 2616, published at 1999, was the most-referenced HTTP 1.1 specs. Unfortunately its description on idempotency was vague, that leaves room for all these debates. But that specs has been superseded by RFC 7231. Quoted from RFC 7231, section 4.2.2 Idempotent Methods, emphasis mine:

    A request method is considered "idempotent" if the intended EFFECT ON THE SERVER of multiple identical requests with that method is the same as the effect for a single such request. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.

    So, it is written in the specs, idempotency is all about the effect on the server. The first DELETE returning a 204 and then subsequent DELETE returning 404, such different status code does NOT make the DELETE non-idempotent. Using this argument to justify a subsequent 204 return, is simply irrelevant.

  1. OK so it is not about idempotency. But then a follow-up question may be, what if we still choose to use 204 in subsequent DELETE? Is it OK?

    Good question. The motivation is understandable: to allow the client to still reach its intended outcome, without worrying about error handling. I would say, returning 204 in subsequent DELETE, is a largely harmless server-side "white lie", which the client-side won't immediately tell a difference. That's why there are ~25% people doing that in the wild and it seemingly still works. Just keep in mind that, such lie can be considered semantically weird, because GET /non-exist returns 404 but DELETE /non-exist gives 204, at that point the client would figure out your service does not fully comply with section 6.5.4 404 Not Found.

    But I want to point out that, the intended way hinted by RFC 7231, i.e. returning 404 on subsequent DELETE, shouldn't be an issue in the first place. 3x more developers chose to do that, and did you ever hear a major incident or complain caused by a client not being able to handle 404? Presumably, nope, and that is because, any decent client which implements HTTP DELETE (or any HTTP method, for that matter), would not blindly assume the result would always be successful 2xx. And then, once the developer starts to consider the error handling, 404 Not Found would be one of the first errors that comes into mind. At that point, he/she would probably draw a conclusion that, it is semantically safe for an HTTP DELETE operation to ignore a 404 error. And they did so.

Problem solved.

Community
  • 1
  • 1
RayLuo
  • 17,257
  • 6
  • 88
  • 73
  • 17
    +1 "idempotency is all about the effect on the server". Meticulously answered. Well done! I'm a 404 believer for subsequent DELETE requests. – nwayve Apr 16 '20 at 20:38
  • 23
    You had me at _`GET /non-exist` returns 404 but `DELETE /non-exist` gives 204_ – aalaap Nov 25 '20 at 09:20
  • 1
    This answer was very useful to make the underlying specifications clear and to bring back the focus on what the RFC says. In the end it also depends on the client (how many different parties will use it, how will they use it etc.) that is going to use these REST APIs. – maulik13 Mar 29 '21 at 13:53
  • 3
    -1 _"So, it is written in the specs, idempotency is all about the effect on the server."_ I ready the specs differently. It is not the **EFFECT ON THE SERVER** (as-in what happens on the server), but the **INTENDED EFFECT ON THE SERVER** (the intention of the caller to happen on the server). – Remy van Duijkeren May 27 '21 at 09:52
  • 1
    See also [RFC 7231, section 4.3.4. PUT](https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.4) about **intent**. It mentions the intent of the caller, not how the server execute it: _"HTTP does not define exactly how a PUT method affects the state of an origin server beyond what can be expressed by the intent of the user agent request and the semantics of the origin server response. It does not define what a resource might be, in any sense of that word, beyond the interface provided via HTTP"_. – Remy van Duijkeren May 27 '21 at 10:07
  • 3
    @RémyvanDuijkeren, your constructive discussion is always welcome. Regarding your comment, I do not see how your emphasis on the word "intend" would make a difference here. That sentence "A request method is considered *idempotent* if the intended effect on the server..." was talking about *idempotency*, not about the status code. While we all agree that HTTP DELETE is idempotent, my point was that the status code is irrelevant. Your other quote from HTTP PUT is also irrelevant because it was in a different context talking about the PUT representation discrepancy between client and server. – RayLuo May 27 '21 at 15:37
  • @RayLuo You define idempotency by the sentence "So, it is written in the specs, idempotency is all about the effect on the server" and then claim it's not about idempotency. That’s why I challenged that specific sentence. Intended effect on the server (by the client) vs the real effect on the server is a significant difference, because that is also including the client-side, which to me it also means status codes. The specs at least doesn't explicitly rule status codes out when defining idempotency. – Remy van Duijkeren Jul 21 '21 at 14:22
  • @RémyvanDuijkeren, so, despite of the specs says idempotency is about the "intended effect on the server", you suggest that "status code is part of the REAL effect on the server"? I would respectfully disagree with such a perspective, yet I do not think either you or I would have more evidence to support/refute it. So, I would suggest you to copy and paste your 3 comments above and combine them into a new answer here, and let the collective wisdom of the community to chime in. – RayLuo Jul 22 '21 at 04:28
  • @RayLuo, what would be the result of applying the same logic to PATCH/POST in this case? If we retry PATCH "SET Name=ABC", so second attempt doesn't trigger the intended EFFECT - would you not suggest to return 200 response? What then? In order to be consistent it would be logical to treat it as client error as well 4xx, wouldn't it? – Sasha Dec 16 '21 at 12:30
  • @Sasha, PATCH/POST are not even defined as idempotent. Client should not blindly retry them in the first place. Regardless, server side should still return true value (be it 200 or 4xx), solely based on whether the request was fulfilled. (P.S.: This is a follow-up question that does NOT really belong to this Q&A. You should start a new question if you have any follow-up questions.) – RayLuo Jan 27 '22 at 21:21
  • I was unsure about what to do in this use case, and this answer clears all my doubts. Well done. – Cristiano Dec 13 '22 at 14:21
  • @Sasha sorry problem is not solved, can you explain how a POST request can effect the state of server? If DELETE request does not change the state of server, why is entity removed from resource, (some information will not be in there anymore)? If you post, you add something, if you delete, you remove something. I see the same behavior from server perspective. – Eric Feb 23 '23 at 13:44
  • "RFC 7231, section 4.3.5 DELETE happened to only mention a successful response should be 2xx, but it did not call out what a subsequent DELETE would get". This to me says it all. Success for a DELETE meas that the resource doesn't exist at the end of the call. This is true also when the resource never existed in the first place. So even a 200 makes sense at all times. A 404 does have the downside of allowing resource exploration with minimal impact. – Andrei Dascalu Jun 21 '23 at 06:18
40

The RESTful web services cookbook is a great resource for this. By chance, its google preview show the page about DELETE (page 11):

The DELETE method is idempotent. This implies that the server must return response code 200 (OK) even if the server deleted the resource in a previous request. But in practice, implementing DELETE as an idempotent operation requires the server to keep track of all deleted resources. Otherwise, it can return a 404 (Not Found).

Yahor
  • 639
  • 8
  • 16
yves amsellem
  • 7,106
  • 5
  • 44
  • 68
  • Yes, that looks like a great resource. However, the DELETE section is not pulling up for me (it is page 23 and the preview has that redacted). Have you read this book? Do you happen to know the answer to my question? – Craig Wilson Jun 22 '11 at 13:06
  • This book is a must have for building REST (it talks in particular, not in a language). – yves amsellem Jun 22 '11 at 13:11
  • 7
    @Craig Reading the Cookbook, it says you SHOULD return 200 OK even if you have deleted it already. However, in practice that would require the server to track all deleted resources, therefore, you CAN use 404. It goes on to say that security concerns may require you to always return 404. Page 11. – Darrel Miller Jun 22 '11 at 13:12
  • +1 Second and highly recommend the book for designing RESTful services. – Paul DelRe Jun 22 '11 at 13:14
  • Awesome thanks, I missed that on page 11. I'll definitely be purchasing the book. – Craig Wilson Jun 22 '11 at 13:19
  • 39
    Well, the book is wrong. Idempotency doesn't imply that the status code will be the same. What's relevant is the final state of the server. – Julian Reschke Sep 21 '12 at 06:42
  • Why can't you just check if status is 204 or 404 and go on your way. My server is going to send back the 204 if it actually deleted something. 404 if it's not found. My client side is just going to do a simple if (204 || 404) and go on it's way. At least the server is being truthful. What the client does with that data is up to the programmer. Both 204 and 404 show the resource isn't there. Let the client decide. That's how I will be programming mine. An example could be... I'm currently writing a test. I KNOW the resource is there, I expect a 204. If I get a 404 something went wrong. – PerryCS Nov 08 '21 at 08:27
  • 2
    cont... it depends on your situation. PayPal returns the same code. Someone else like me, wants to know if it found it to delete it. I programmed the client side, I'll handle it how I need to handle it. In some cases I DO want to know. In other cases it doesn't matter what is returned as long as it's not there anymore. Some examples say do it this way, others say the other way. It's up to you and your situation. If you never need to care if it deleted something or not... return the same code, make your client code more simple as long as it fits into your project :) – PerryCS Nov 08 '21 at 08:37
20

First DELETE: 200 or 204.

Subsequent DELETEs: 200 or 204.

Rationale: DELETE should be idempotent. If you return 404 on a second DELETE, your response is changing from a success code to an error code. The client program may take incorrect actions based on the assumption the DELETE failed.

Example:

  • Suppose your DELETE operation is part of a multi-step operation (or a "saga") executed by the client program.
  • The client program may be a mobile app performing a bank transaction, for example.
  • Let's say the client program has an automatic retry for a DELETE operation (it makes sense, because DELETE is supposed to be idempotent).
  • Let's say the first DELETE was executed successfully, but the 200 response got lost on its way to the client program.
  • The client program will retry the DELETE.
  • If the second attempt returns 404, the client program may cancel the overall operation because of this error code.
  • But because the first DELETE executed successfully on the server, the system may be left at an inconsistent state.
  • If the second attempt returns 200 or 204, the client program will proceed as expected.

Just to illustrate the use of this approach, the HTTP API style guide for PayPal has the following guideline:

DELETE: This method SHOULD return status code 204 as there is no need to return any content in most cases as the request is to delete a resource and it was successfully deleted.

As the DELETE method MUST be idempotent as well, it SHOULD still return 204, even if the resource was already deleted. Usually the API consumer does not care if the resource was deleted as part of this operation, or before. This is also the reason why 204 instead of 404 should be returned.

Community
  • 1
  • 1
Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
  • 1
    The question is, what is important to the client, that _it_ deleted the resource, or that the resource has been deleted. What if some other client deleted the resource during the saga. Do you really want to fail considering the clients objective has been achieved? – Darrel Miller Jul 20 '17 at 02:42
  • 1
    @DarrelMiller Good point. What is more important depends on the business context. But in general, I'd rather return 204 on a second DELETE attempt, even if the resource was deleted by another client. I don't want the service to fail (i.e., 404) given that the clients objective was achieved. – Paulo Merson Jul 21 '17 at 17:06
  • 2
    As others mentioned, idempotency is not what your response code is, it is what your server state is. – Niranjan Mar 15 '18 at 05:40
  • @Niranjan I agree idempotency is about the server state, but a different response code may drive the client to change server state unnecessarily by cancelling an ongoing saga. – Paulo Merson Mar 15 '18 at 10:51
  • @Paulo Merson what code will you return if the client asks for deletion of a item that NEVER existed ? 204 ? or 404 ? If you always return 204 what is the point in checking return code ? – frenchone Sep 21 '18 at 14:26
  • 1
    @frenchone If you have a way to know that the item never existed, you should return 404 in the first and subsequente DELETE attempts. If you don't, but the client program needs to know whether the item existed, you can have the client program always do a GET prior to the DELETE. GET of course will return 404 if the item doesn't exist (because it never existed or because it was deleted). – Paulo Merson Sep 24 '18 at 18:13