Search code examples
resthttp-putidempotent

How to rename resources in an idempotent manner?


I implemented an API that renames a company as follows:

PUT /companies/A
{
  "name": "B"
}

will return HTTP 301 with the Location header pointing at the company's new URI: /companies/B.

How can I make this operation idempotent with and without If-Match headers?

  1. Without If-Match header: if a user tries to rename a non-existent company, I'd expect the server to return HTTP 404 but I can't do so because then legitimate rename operations wouldn't be idempotent (they'd return 301 the first time, and 404 on subsequent invocations). This is problematic because I want clients to be able to differentiate between a failed renames (the company doesn't exist) versus a rename that had already taken place.

  2. With If-Match header: if the company's ETag depends on the company name, then subsequent rename operations will fail because the precondition no longer holds. Again, this makes it seem that the operation failed when in fact it already took place.


Solution

  • The PUT operation succeeds and should return a 200 or 201. Subsequent requests for the same resource should return a 301 with the appropriate response body indicating the URI of the new resource.

    404 should only be for resources that truly can't be found, i.e. companies that don't exist and never have.

    As noted in the protocol, idempotence doesn't mean that the call returns the same thing all the time. It means there are no side effects. Also, idempotence isn't applicable under error conditions, which anything other than 2xx (like 301) is.

    I really do admire the commitment to getting it right by the spec, but as with all things, it is subject to interpretation.