RESTful way to create multiple items in one request
Asked Answered
T

7

148

I am working on a small client server program to collect orders. I want to do this in a "REST(ful) way".

What I want to do is:

Collect all orderlines (product and quantity) and send the complete order to the server

At the moment I see two options to do this:

  1. Send each orderline to the server: POST qty and product_id

I actually don't want to do this because I want to limit the number of requests to the server so option 2:

  1. Collect all the orderlines and send them to the server at once.

How should I implement option 2? a couple of ideas I have is: Wrap all orderlines in a JSON object and send this to the server or use an array to post the orderlines.

Is it a good idea or good practice to implement option 2, and if so how should I do it.

What is good practice?

Tameika answered 4/1, 2009 at 18:42 Comment(0)
E
101

I believe that another correct way to approach this would be to create another resource that represents your collection of resources. Example, imagine that we have an endpoint like /api/sheep/{id} and we can POST to /api/sheep to create a sheep resource.

Now, if we want to support bulk creation, we should consider a new flock resource at /api/flock (or /api/<your-resource>-collection if you lack a better meaningful name). Remember that resources don't need to map to your database or app models. This is a common misconception.

Resources are a higher level representation, unrelated with your data. Operating on a resource can have significant side effects, like firing an alert to a user, updating other related data, initiating a long lived process, etc. For example, we could map a file system or even the unix ps command as a REST API.

I think it is safe to assume that operating a resource may also mean to create several other entities as a side effect.

Estelleesten answered 17/11, 2015 at 22:20 Comment(6)
I aggree with this. You should abstract out the concept of a collection of your resource and treat that like a resource. This will give you more flexibility in the future as well, when you want to start doing operations on this etc.Dryclean
This is the right approach. This doesn't break POST Collection request. Since, it is used to post a single entity. Sending a bulk request with a "separate bulk entity" is the right approach.Outwit
Interestingly, people recommend using the plural form (of the collection) in the URL when you want to create a single resource, like so: send a POST to /api/books to create a book. But then when you want to create 100 books (in a single request as json), which URL would you post the collection of 100 books to? that's where the restlessness begins.Marsala
@Marsala you could use /api/book-group, /api/book-collection or something similar.Estelleesten
how would the response look like in this case? if we abstract the collection as a resource then you would return the flock id/url? but there is no flock. so there is no id. so should we instead return { success: [ ], failure: [ ] } with the sheep id's in the appropriate collection?Agist
@shamanthGowdraShankaramurthy I don't think that a collection must have an id. I would just return the representation of the created entities and perhaps a list of errors if existing.Estelleesten
R
58

Although bulk operations (e.g. batch create) are essential in many systems, they are not formally addressed by the RESTful architecture style.

I found that POSTing a collection as you suggested basically works, but problems arise when you need to report failures in response to such a request. Such problems are worse when multiple failures occur for different causes or when the server doesn't support transactions. My suggestion to you is that if there is no performance problem, for example when the service provider is on the LAN (not WAN) or the data is relatively small, it's worth it to send 100 POST requests to the server. Keep it simple, start with separate requests and if you have a performance problem try to optimize.

Rozalie answered 8/1, 2009 at 19:40 Comment(6)
Did you find a solution yourself for errors in case of batching? On a mobile connection sending 100 post requests to show a page seams like a bad idea.Mahogany
I append the errors to an array, route the user to a 419 Conflict error page (and return that error to the client), and display the array of errors. See my answer below for more details.Berke
This is nonsense. The question is about sending an order for many items, which as many have said, you can just to in the entity of one POST request. How the server handles that is another thing entirely. In this case, I see no problem with creating an order, populating what you can for that order, and also populating details that could not be carried out. That way a user can then see their order, and see that all but N items were added to the order, but that some were out of stock, or the system did not know what to do with. Another simpler but less user friendly option is to reject everythingSquatness
@Squatness a lot changes in 3.25 years. You should probably post a completely formulated answer to the question.Showthrough
@Showthrough yeah, I should probably do a lot of things... I'll get to it at some stage maybe...Squatness
There are some scenarios where I could think POSTing a collection is more efficient. For example if you're providing a CRUD web UI for adding a list of Products (SKU, description, unit price) using HandsOnTable (a JS spreadsheet library), in this case the user expects just to copy/paste a batch or bulk of products at once. You can of course handle that with the JS client and make 1 request at the time but gets more complicated IMO, I personally like to leave that work to the backend REST API.Eugenaeugene
J
12

Facebook explains how to do this: https://developers.facebook.com/docs/graph-api/making-multiple-requests

Simple batched requests

The batch API takes in an array of logical HTTP requests represented as JSON arrays - each request has a method (corresponding to HTTP method GET/PUT/POST/DELETE etc.), a relative_url (the portion of the URL after graph.facebook.com), optional headers array (corresponding to HTTP headers) and an optional body (for POST and PUT requests). The Batch API returns an array of logical HTTP responses represented as JSON arrays - each response has a status code, an optional headers array and an optional body (which is a JSON encoded string).

Jehanna answered 16/9, 2014 at 19:18 Comment(4)
This is very interesting link, proposed solution seems usable to me. Anyway, at StackOverflow preferred answer is explaining the concept of the solution in the body of an answer as links may change or disappear.Phosphorate
That's really Facebook's way of doing it, not necessarily RESTful as the OP askedSuboxide
I think a Batch API (from Google, Facebook, etc - @PuneetArora) is more useful when grouping several unrelated requests together. Creating a request that creates one item, and then grouping all those requests together to send a collection of items is "insanity" (Einstein). Just create a request that passes a collection of items.Selimah
I like this way the best-- each resource is handled as a separate request but there's still one request sent as far as I'm concerned, correct?Lagting
K
9

Your idea seems valid to me. The implementation is a matter of your preference. You can use JSON or just parameters for this ("order_lines[]" array) and do

POST /orders

Since you are going to create more resources at once in a single action (order and its lines) it's vital to validate each and every of them and save them only if all of them pass validation, ie. you should do it in a transaction.

Karolyn answered 4/1, 2009 at 20:23 Comment(0)
B
7

I've actually been wrestling with this lately, and here's what I'm working towards.

If a POST that adds multiple resources succeeds, return a 200 OK (I was considering a 201, but the user ultimately doesn't land on a resource that was created) along with a page that displays all resources that were added, either in read-only or editable fashion. For instance, a user is able to select and POST multiple images to a gallery using a form comprising only a single file input. If the POST request succeeds in its entirety the user is presented with a set of forms for each image resource representation created that allows them to specify more details about each (name, description, etc).

In the event that one or more resources fails to be created, the POST handler aborts all processing and appends each individual error message to an array. Then, a 419 Conflict is returned and the user is routed to a 419 Conflict error page that presents the contents of the error array, as well as a way back to the form that was submitted.

Berke answered 24/10, 2013 at 15:40 Comment(0)
T
6

I guess it's better to send separate requests within single connection. Of course, your web-server should support it

Trigon answered 25/6, 2009 at 0:10 Comment(0)
R
-5

You won't want to send the HTTP headers for 100 orderlines. You neither want to generate any more requests than necessary.

Send the whole order in one JSON object to the server, to: server/order or server/order/new. Return something that points to: server/order/order_id

Also consider using CREATE PUT instead of POST

Ruhl answered 4/1, 2009 at 20:4 Comment(7)
I suppose he ment HTTP POST method. There is no such a thing as CREATE HTTP method.Karolyn
Isn't there? Oh wait, there weren't. There were PUT instead.Ruhl
Why on earth would you use PUT for creating content? This is exactly what the HTTP POST method is for.Squatness
You use PUT to create resources when you want the client to specify the URI of the resource, like in webdav. I don't agree with the poster's use of PUT, but it does have a place in creating resources, although that place may be limited in scope.Calling
@Squatness i think he means that he's modifying the collection via PUT but this should replace all the resources (not to add resources). So using the same logic, we should use PATCH instead, considering that we're modifying the collection partially by adding new resources.Renfred
@Renfred No, these are brand new order, there is no existing order to 'PATCH' and so either PUT or POST should be used. In this case, you URL would be like /orders, you do not care what URL each individual order is assigned, so POST would be the choice. Now, arguably yes, he is modifying the entire collection of orders, however 'orders' is not a singular resource and by that logic you could argue everything is part of the root resource.Squatness
Note: POSTing an entity should result in the entity becoming a subordinate of the resource addressed in the request and is not idempotent. PUT replaces the entity at the address and is idempotent. The idempotency (word?) is an important expectation for consumers.Sepaloid

© 2022 - 2024 — McMap. All rights reserved.