Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Design a Restful API for Bulk Inserts and Updates?

I have a Web API application and I'm using the below url for both bulk (tens or hundreds) inserts and updates which return just OK or Failed.

POST api/v1/products

which is mapped to my action:

public HttpResponseMessage PostProducts(PostProductsRequest request)
{

...
}

PostProductsRequest object contains a Products property of type List.

If the Id property exists for a Property, I update it otherwise it'd indicate an insert.

But I'm just wondering whether I should use Post for Bulk Inserts only and Put for Bulk Updates, not sure. What's the best practice and advantage of each approach?

How to Design a Restful API for Bulk Inserts and Updates?

like image 813
The Light Avatar asked Jun 25 '13 08:06

The Light


4 Answers

Either method can be used, depending on your requisites, but this doesn't mean they don't have significant differences. HTTP methods are not CRUD. PUT or POST are not Create and Update, or the other way around.

PUT completely replaces the resource at the given URI with the entity provided, so it can be used to create and also to update, but only if it contains the full representation. A GET request made immediately after a PUT should return the same resource. The representation may be exactly the same, although it's possible for the service to add default values that were missing from the PUT'ed representation.

POST tells the server that the entity being provided is subordinated to the resource at the given URI, and they have an agreement on what it should be done with that. It might be anything, a create, an update, any operation that isn't standardized by HTTP itself.

With this in mind, a bulk insert or update with PUT is only RESTful if you're replacing the whole collection identified by the URI. This doesn't have to be necessarily your whole collection associated with that media type. The URI can have a querystring slicing the dataset, and you perform the bulk operation on that slice only.

For instance, if you have the following collection resource:

GET /api/products

Represented by:

{'products': [product1, product2, product3]}

And you want to add three more products, a bulk operation with PUT would have to append your new products to the existent and send the whole collection back:

PUT /api/products

{'products': [product1, product2, product3, product4, product5, product6]}

However, if you have a filter constraint you can apply to /api/products that would return an empty collection on the GET above, then it would be fine to do the PUT only with the new products to that filtered resource. For instance, let's say the products above can be filtered by a partner attribute, they have partner x and you're adding for partner y:

In that case, it's fine for you to do:

PUT /api/products?partner=y

{'products': [product4, product5, product6]}

And a GET /api/products after that returns:

{'products': [product1, product2, product3, product4, product5, product6]}

As long as GET /api/products?partner=x returns:

{'products': [product1, product2, product3]}

And GET /api/products?partner=y returns:

{'products': [product4, product5, product6]}

This might seem complicated and sometimes it looks like it's better to use POST instead of PUT, but keep in mind that the whole operation above is standardized. It's using PUT exactly as it's intended to be used. The operations can be more straightforward with POST, but they are not standardized and you'll have to design and document your own syntax for it.

like image 86
Pedro Werneck Avatar answered Oct 31 '22 14:10

Pedro Werneck


I would recommend to use POST to create and PUT to update (actually create or update as it is indempotent).

From the RESTful Webservices Cookbook (O'Reilly):

Use POST and a collection resource to create a number of similar resources at once. Let clients include information about the resources to be created in the request. Assign a URI for all the resources created, and redirect the client to the collection using response code 303 (See Other). A representation of this resource includes links to all the newly created resources.

To update or delete a number of similar resources in bulk, use a single URI that can return a representation containing information about all those resources. Submit a PUT request to that URI with information about the resources to be updated or a DELETE request to delete those resources. In all these cases, ensure that the processing of the request is atomic.

like image 30
benjiman Avatar answered Oct 31 '22 16:10

benjiman


I just happened to be looking at the HTTP 1.1 method definition and was reminded of this question.

The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.

This would indicate to me that if you were to use PUT and the payload contained a not existing resource with enough information to create it then it should be created and therefore PUT would be the correct method verb in a bulk operation that can create & update resources.

like image 3
Rich Andrews Avatar answered Oct 31 '22 15:10

Rich Andrews


The most "standards compliant" way for batch operations in RESTful Web services is to use one of the various 'collection' approaches (i.e. DELETE /mail?&id=0&id=1&id=2) or you could use a batching handler to simply the process.

Honestly, I use the exact same pattern as you do except for the fact that I use POST for object creation and PUT for updates only (which is the standard way of doing it). Also POST should return 201 - Created along with the created object and PUT should return 204 - No Content with no data, if the operation succeeds. Of course, as you're doing bulk creation, you may opt not to return the array of newly created objects with POST.

To summarize it:

POST api/products
  |
  |---> Success: 201 [NewObject1, NewObject2, ...]
  |---> Failure: Relevant error code as to why the operation failed

PUT api/products
  |
  |---> Success: 204
  |---> Failure: Relevant error code as to why the operation failed

Update: vNext of ASP.NET Web API will have batching built in!

like image 2
Teoman Soygul Avatar answered Oct 31 '22 15:10

Teoman Soygul