Idempotent POST

I noticed a very old thread about idempotent POST that went dead #764.

I think it is a common use case to have natural keys (uniqueness constraints that identify unique resources), but to allow the system to provide a generated id. I POST the resource and get a 201 Created with Location. Great.

Its also very common that this could happen many times concurrently or in quick succession. In this semantic I don’t want to return a 409 conflict (I may in other cases), I want the client logic to be race agnostic -or at least ambivalent. The semantic of course is data model dependent.

I see 2 choices (you may suggest others):

  1. Lie and say I created it the second/third/fourth time, even though I didn’t and return 201 Created + Location (same location each time)
  2. Be honest, return 201 Created+ Location for the first POST and 200 OK + Location (same location each time) for subsequent ones.

I think option (1) violates the spirit of the jsonapi spec, but make client handling very slightly easier, whereas option (2) appears to be acceptable, and the client handling is still pretty clean.

I’m pressing forward with (2) but very keen to hear thoughts and ideas from the community experienced in using jsonapi.

I have been in a similar situation, and have chosen (and been able) to return 409.

Neither of the other options are optimal. Both break the spec and therefore have the possibility of breaking clients, though IMHO they’d have to be fairly strict or make very specific assumptions in order for that to happen. In any case, make sure you clearly document any way your API deviates from the JSON:API spec.

A drawback of both approaches is that if someone POSTs with attribute A set to “foo” and the resource already exists with A set to “bar”, what do you return? Do you also update the resource and set A to “bar”, or do you skip updating the value and return the resource with A = “foo”?

Aside from these two options, my only suggestion is to reconsider if you really cannot return 409. Since you haven’t given concrete information about your use-case, only you can answer that. Could you change anything else to make it less likely that multiple clients are attempting to create the same resource?

1 Like

Hi, thanks for taking the time to reply.

Regarding considering 409.

In the case of a POST with a natural unique key and content that does not exactly replicate that which already exists - then it is not a question of idempotency, so i’d return 409. I think this is fine with the specification and do this already in many places when uniqueness constraints are violated.

If the intention is to PATCH atrributes to different values, the client should the retry with a PATCH. Hopefully the 409 carried enough information to allow the client to do this. I don’t think there is any built in syntax to support this, but this is not my problem today.

The special case that concerns me is that a second POST that is logically identical (modulo internal caches or aspects not consideed important for the semantics, such as a “created” timestamp), I want to return saying OK, you got what you wanted, but by the way it wasn’t you that did it (400 instead of 402).

If I instead returned a 409, there is no standard way to encourage the client not to give up, and most solutions would involve a lot of non-standard design of the error message, or a number of additional calls from the client. This, to me, feels like a lot of non-standard work to express something that is easily acheived with a 400 (and an explanation in my OPENAPI document of how to interpret the 400)

I’m not keen on the 402 because it is factually incorrect.

In this case I have automated processing of incoming messages that is splitting information across microservices - there are many-to-one relationships where the “one” is learned through presence in the incoming messages. Even if each thread or application queries first to see if it exists, it would be racing many other procesing workers. I feel the “create if required” semantic is pretty common.

Not a bad idea!

JSON:API supports code in the error, which is useful to govern client behavior. You can return a specific documented code with the 409 error when the resource exists (e.g. set code to resourceAlreadyExists), and the client can then use that to inform its next steps (either ignore it if they just wanted to make sure it exists, or do a GET request for the resource if they need it, or do a PATCH if they wanted to change some fields).

I’d be keen to understand if there are sets of error codes and object structures that have been standardised in some way. I’ve not seen resourceAlreadyExists in the jsonAPI standard.

Of course I’m aware I can make error codes and structures up. In the bad old days that was all we ever did, but then every single application behaved in its own special way all the time requiring a lot of careful investment in understanding the dark corners of the API spec.

Other than that, I have satisfied myself that my solution works for me, so thanks again for sharing your thoughts.

AFAIK this is not standardized in any way. You’d have to document this for your API(s).

Just in case, note that I’m not talking about HTTP response codes. You should stick to the standard ones. As for “structures”, I’m not sure what you mean, but as you likely know, JSON:API has a specific error format it uses. For telling the API clients how to behave, use the error object’s code property, which is the only one intended for programmatic parsing to differentiate various error conditions. If you need to supply any needed additional data, do that in the error’s meta.