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?

7 Answers7

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.

miguelcobain
  • 4,734
  • 4
  • 32
  • 45
  • 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. – villy393 Oct 06 '16 at 10:28
  • 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. – Sorter Oct 18 '16 at 11:54
  • 2
    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. – code4kix Oct 17 '18 at 13:54
  • @code4kix you could use `/api/book-group`, `/api/book-collection` or something similar. – miguelcobain Oct 17 '18 at 14:31
  • 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? – shamanth Gowdra Shankaramurthy Oct 15 '21 at 16:50
  • 1
    @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. – miguelcobain Oct 18 '21 at 00:48
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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
LiorH
  • 18,524
  • 17
  • 70
  • 98
  • 4
    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. – Thomas Ahle Aug 26 '11 at 08:24
  • 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. – Eric Fuller Oct 24 '13 at 15:45
  • 9
    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 everything – thecoshman Mar 12 '14 at 15:16
  • 2
    @thecoshman a lot changes in 3.25 years. You should probably post a completely formulated answer to the question. – dlamblin Jul 21 '14 at 21:44
  • 3
    @dlamblin yeah, I should probably do a lot of things... I'll get to it at some stage maybe... – thecoshman Jul 22 '14 at 07:58
  • 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. – ivansabik Apr 30 '17 at 23:43
11

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).

Kzqai
  • 22,588
  • 25
  • 105
  • 137
rwitzel
  • 1,694
  • 17
  • 21
  • 2
    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. – Jan Vlcinsky Mar 15 '16 at 21:57
  • 13
    That's really Facebook's way of doing it, not necessarily RESTful as the OP asked – 0cd Mar 07 '17 at 02:58
  • 2
    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. – tim-montague Aug 01 '17 at 17:03
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.

Milan Novota
  • 15,506
  • 7
  • 54
  • 62
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.

Eric Fuller
  • 159
  • 1
  • 8
6

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

zakovyrya
  • 9,579
  • 6
  • 39
  • 28
-4

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

dlamblin
  • 43,965
  • 20
  • 101
  • 140
Cheery
  • 24,645
  • 16
  • 59
  • 83
  • I suppose he ment HTTP POST method. There is no such a thing as CREATE HTTP method. – Milan Novota Jan 04 '09 at 20:27
  • Isn't there? Oh wait, there weren't. There were PUT instead. – Cheery Jan 04 '09 at 22:00
  • 26
    Why on earth would you use PUT for creating content? This is exactly what the HTTP POST method is for. – thecoshman Jun 27 '13 at 15:14
  • 9
    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. – user602525 Jan 12 '15 at 19:25
  • @thecoshman 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. – MhdSyrwan Apr 15 '15 at 12:58
  • @MhdSyrwan 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. – thecoshman Apr 17 '15 at 07:32
  • 2
    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. – Luke Puplett Jan 05 '16 at 14:22