Work around associative entity (relationship)


#1

For a typical data model
`CREATE TABLE products (
product_no integer PRIMARY KEY,
name text,
price numeric
);

CREATE TABLE orders (
order_id integer PRIMARY KEY,
shipping_address text,

);

CREATE TABLE order_items (
product_no integer REFERENCES products,
order_id integer REFERENCES orders,
quantity integer,
PRIMARY KEY (product_no, order_id)
);`

jsonapi may expose orderItem as a restful resource. To add items to an order my client must perform a POST request for each item to add to an order and a final request to submit the order. Is it possible instead to PATCH the order with the required information? I feel like this is a common pattern.


#2

Do you mean that instead of repeatedly POSTing to create lines as new resources you’d like to repeatedly PATCH to add lines as new attributes? Or are suggesting that a single PATCH could both add a line and submit the order in a single operation?


#3

The latter. I was hoping I could patch an order along with some relationships. Or implement a non jsonapi endpoint that does all the work api side.


#4

I suppose you could, but that seems rather odd to me. I’ve seen workflows where you gradually build up an order then finally submit it, and workflows where you automatically submit the order at the point of creation (when receiving a list of lines); but I’ve never seen the scenario where a user has an “add to cart and submit order” button.


#5

Well we don’t want exactly that. We want to avoid synchronization of http requests client side. And so, would prefer collecting information from the user (what products do they want?) and when they click submit have the order be submitted.

The whole concept of orderItem only complicates the client side code and so I feel like this is one of those perfect opportunities for API design to simplify our work, not complicate it.


#6

Sorry, I’m a bit confused now. Can you list out what steps you would ideally like your client to perform, and which steps you feel JSON API might not support?


#7

say we have:

/products
/orders
/orderItems

in order to “submit” an order we currently POST /orderItems (once per item, which can be many). When the user is ready to submit an order we can PATCH /orders/%d to change its status to checked out.

This requires the client to perform many http requests. Could there be an alternative flow that allows the client to be more declarative and state “products A, B and C should go into order O and checkout” in a single HTTP request?


#8

Yes. And I think you have two options here:

  1. Start with no order, then create one containing products A, B, and C, with a status of checked out in a single POST request. (I think this is a reasonably common approach, though less common than the multiple POST requests to add line items approach.)
  2. Have an existing order (maybe in an unconfirmed status) already with some products, then add products A, B, and C, and set the status to checked out status in a single PATCH request. (I think this is a much rarer approach, and I suspect may lead to complications around validation and transactions, but JSON API permits it.)

#9

could you chime in over at https://github.com/json-api/json-api/issues/795#issuecomment-224625507

It sounds like for option 1 above you are suggesting embedding the products within the order model. What would the payloads look like for options 1 and 2?

The crux of the issue seems to be that id’s are required in http://jsonapi.org/format/#crud-creating we need a facility to pass in items in the relationships array without ids and have the api be smart enough to create them. According to the spec a Resource Identifier Object MUST have and id.


#10

Something like this:
{data: {"type": "order", "id": "O", "attributes": { "status": "checked_out", "line_1": {"productID": "A", "productQuantity": "5"}, "line_2": {"productID": "B", "productQuantity": "3"}, "line_3": {"productID": "C", "productQuantity": "5"} } } }
So the line items are now attributes of the order rather than related to it. This seems reasonable if the order lines can no longer be manipulated by the client; maybe the entire order can be cancelled after checkout (but before delivery), but maybe the line items can’t be updated at this point.

Of course, if your backend systems also use the same API this may be a problem because they may need to update the order at line level (e.g. if there is a problem fulfilling the order due to insufficient stock).

In that case (i.e. where the order is still amendable by other API clients) in scenario 1 at least you could chose to make the above payload an “order request” rather than an order, and have the API convert this into separate “order” and “line item” resources behind the scenes. In scenario 2 this would not work, but I’m a bit uncomfortable with that approach anyway.