3

I'm trying to create a REST API that allows the user to:

  • create a resource if it doesn't exist.
  • update a resource if it exists.

with the same request.

Given an example: I have a company that has many employees. I know that the way that usually an employee is inserted is using something like do a post request to /employees/ URL with a body that represents the employee to be created and as result, I will get similar content with a field that identifies the created resource. In most cases, if I try to insert another employee with data that conflicts with the first created employee, like the fiscal document code, I will get a 400 or 409 response that indicates a conflict with an existent resource. But if I want to create a permissive API that instead updates the present resource according to an attribute that I know that uniquely identifies the resource, what is the best approach?

  • A POST request means that the request is not idempotent, but an "upsert" request is idempotent. So I think that design the API using the POST method is not adequated.

  • A PUT request is idempotent but refers to a specific resource or a set of resources. Semantically, do a PUT /employees/ means that all existing employees are matched in this request.

  • Use something like PUT /employees/123, that 123 is an identifier of the employee is not possible when its identifier is synthetic and generated by the service. The user cannot know what identifier to use in this case.

Is possible to create a RESTful API that allows this kind of "upsert" request? If possible, how can I do?

3
  • The easiest solution for your problem is to ditch sequence identifier in favour of uuid. That way you can have a simple PUT /employees/{employeeUuid} endpoint, generate the identifier on the client and create a resource if it does not yet exist, otherwise update it (with access checks, if necessary).
    – Andy
    Commented May 10, 2021 at 13:30
  • 1
    I don´t think it's a good solution. First, because it requires a presence of UUID that can be inconsistent with the others resources. But also it breaks with REST. If already exists a resource, that resource will have its own UUID, which will be different than the requested UUID. A request that puts the data in the resource xyz and doesn't updates nor creates the xyz resource, but instead updates the resource abc and returns it, does not comply with REST. Commented May 10, 2021 at 14:01
  • The client and server must have a way of uniquely identifying an employee other than the server generated ID, otherwise when they did a POST you wouldn't know to update an existing employee or create a new employee. So I would second Andy's suggestion of a UUID. What ever way the client and server uniquely identifies a resource (maybe name, date of birth etc) turn that into a UUID on the client side. The server can use the same algorithm to generate the UUID on the server side. Thus no conflicts. Then just use PUT for everything Commented May 17, 2021 at 19:09

1 Answer 1

3

There is no rule stating that the behaviour of your POST handling may not be idempotent. It is just that all other methods are considered as "must be idempotent", but that doesn't mean POST isn't allowed to be idempotent.

So you wouldn't violate any rules if you would implement POST to also function as an upsert operation. However, be very careful about this. What will you do if Alice her fiscal document is the same as Bobs, but her social security number is the same as Charlies? So you update Bob, Charlie, both, neither, or do you merge them somehow?

Looking at things from an API standpoint, the cleanest way to do this would probably be to have POST /employees/ response with 307 Temporary Redirect + Location: /employees/123 - that way the client can decide to overwrite whatever is there, or go a GET /employees/123 first so somebody (or maybe just some code) can compare the two to decide if overwriting that data is safe.

Another option all together, if you source system is 100% in charge of what employees exist, is to use some unique reference from the source system (say "xyz") and always PUT employees (it doesn't have to be a PUT /employees/xyz, it could be PUT /source-system-employees/xyz to signify that the identifier used there is specific to the source system, letting you use /employees/123 in places that have nothing to do with that source system).

Your Answer

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

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