Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice to batch update a collection in a REST Api call [closed]

Hi I'm looking for best practices with API design to batch update a collection with an API call.

My collection has URL /api/v1/cars and I would like to update all the cars in the collection to add a timestamp of the current time.

{
    data: [
    {
        manufacturer: 'Porsche',
        timestamp: ...
    },
    {
        manufacturer: 'BMW',
        timestamp: ...
    }
    {
        manufacturer: 'Peugeot',
        timestamp: ...
    }
}

I thought about a few options but I can't figure what is the best practice.

Should it be:

1/ Modelled as another resource such as POST api/v1/cars/updateTimestamp

2/ Passed as a query parameter: PUT api/v1/cars?updateTimestamp

3/ Pass in the body of the request:

POST api/v1/cars 
{"operation":"Update timestamps"}

I'd like to emphasize that the whole processing should be done on the back-end and not passed by the client. Same problem would happen for any complex processing that happens on the back end.. How could I keep the API RESTy in this case.

Thanks a lot for your help/any pointer to relevant ressources.

like image 613
jhagege Avatar asked Sep 14 '25 11:09

jhagege


2 Answers

As there is no partial PUT defined in HTTP, you either need to send the whole entity for each resource to update or use some other operations.

POST

As POST is an all-purpose operation you can use it to create a short-living temporary resource at the server (which further does not even have to have an own URL). On receipt the server can update either all specified entries or all entries in general with some provided field-value combination (certain table alterering may be necessary though if the column is not yet know in relational databases)

A simple request may look like this:

POST /api/v1/cars/addAttributes HTTP/1.1
Host: example.org
Content-Length: 24
Content-Type: application/json
If-Match: "abc123"

{
  "timestamp": "..."
}

This approach has the advantage that it could be sent to the server anytime, even without prior knowledge of the current state. This, however, also has the danger that certain entries get updated which shouldn't be affected. This can either be influenced by specifying an If-Match header, which points to a certain version-hash and is changed on every entity update, or by adding a certain restrictor to the JSON body which the server also has to understand.

PATCH

Similar to POST, which can literally send anything to the server, PATCH is intended for modification of resources. A single request explicitely may update multiple resources at once, however, the client needs to define the necessary steps to transform the resources from state A to state B. Therefore, the client also needs to have the latest state of the resource in order to successfully transform the resource to its newest state (-> ETag and If-Modified HTTP headers)

A JSON Patch request for the provided example may therefore look like this:

PATCH /api/v1/cars HTTP/1.1
Host: example.org
Content-Length: 215
Content-Type: application/json-patch+json
If-Match: "abc123"

[
  { "op": "add", "path": "/data/1", "value": [ "timestamp", "..." ] },
  { "op": "add", "path": "/data/2", "value": [ "timestamp", "..." ] },
  { "op": "add", "path": "/data/3", "value": [ "timestamp", "..." ] }
]

Where /data/1, /data/2 and /data/3 are the unique identifiers for the resources of Porsche, BMW and Peugeot.

PATCH specifies that a request has to be atomic which means that either all or none of the instructions succeed which also brings some transaction requirements to the table.

Discussion

As already mentioned, in POST requests you can literally send anything you want to the server at least as long as the server is capable of understanding what you are sending. It is therefore up to you how you design the request structure (and the server logic as well).

PATCH on the other hand, especially with JSON Patch defines some strict rules and predefined operations typically to traditional patching. The transaction requirement can be a burden but also a benefit. In addition to that, PATCH is not yet final and still in RFC though it is already widely available.

like image 146
Roman Vottner Avatar answered Sep 17 '25 07:09

Roman Vottner


I would do a PUT to do an update operation. PUT is for updating objects, POST for creating. You could specify fields using query parameters.

  • POST /api/v1/cars > create new cars
  • PUT /api/v1/cars > update car based on id in the object. You could also create a cars/:id route if you want to use the route without an id to do an update on all car objects (which seems not to happen in many applications).
  • GET /api/v1/cars > list all cars
  • GET /api/v1/cars/:id > list one car.

To answer your question. You want to update all car objects. In that case I would use the PUT /api/v1/cars and specify a /api/v1/cars/:id to do an update on one car, although I do not see why you want to implement this. If it is a one time operation, I would update all cars in the DB instead of creating an API route.

like image 36
Bram Avatar answered Sep 17 '25 07:09

Bram