Expressing side effects in responses


#1

When an action has side effects (non-relationship) how should these be reported in a response?

e.g.

I have a shopping cart that contains a collection of items.

Items can be products, promotions/discounts, shipping charges etc.

I have a 2 for 1 discount and 2 items that are applicable, if I remove one of those items how should I represent the removal of the discount item in the response?

Any tips/guidance gratefully received.


#2

I would deal with that within the client.

The UI should make it clear that those two items cost the price of one. If the user deletes one of the item, then the price is adjusted.


#3

thanks for the response maark…

Ui is not an option here unfortunately - this is an api so stripping it all back I suppose the question is what is the best way to handle a request that has side effects where relationships are not involved.

My initial thoughts were to return 207 (predicated on the primary purpose of the request being successful - otherwise we’d error and the side-effects would not be a consideration)) and then detail each side-effect in the meta-data of root of the response.

In the [poor] example I give in the OP - the promotion_item would be removed because one of the items it relates to has been removed and therefore the requirements of that promotion are no longer fulfilled.

I’m struggling to think of any other way that my initial idea that would satisfy this requirement.

I do hope others have some ideas too - this feels like something that would be useful and also benefit from the minds of teh jsonapi community to make it useable/sensible


#4

Not sure which example you are talking about, but this sounds like a good idea.

You have an order which contains items. You have a promotions relationship too. Whenever a promotion applies to your order, you add it to the list of promotions. When an item is removed, you can remove the promotions that are associated to this item from the promotions relationship.

Whoever is using the API can find a list of active promotions on any order and when they modify on order, they can compare the new order to see the promotions that were added or removed.


#5

I’m keen to here others thoughts on the 207 response code and then further details in the meta-data solution - is it a valid use of meta-data?

What other possible solutions could be applied?


#6

So, I realize I’m late to this party but starting from the top.

I would rethink your design if you believe a shopping cart is a collection of polymorphic items which have such substantially different semantics which would require an intelligent client to be reactive to the representation schema itself. One item is an item, another is a conditional price modifier, another is a fee, and your list continues and that will make the requirements of your client get ever more complex. The cart should be a container with relationships to many resources of the known qualities to prevent ambiguity and reliance on duplication of your specific business logic.

Your premise is a side effect of the ambiguity inherent in trying to rehydrate the semantic differences you casually discarded in the design of the cart in the first place. The solution in your example isn’t to come up with a clever means to solve the problem, but fix the design and then you won’t have the problem.

To further illustrate the point, this should apply the discount to the items (not removing anything), and it should be defined with preconditions (inputs) and the expected output (negative line item price). With that design there isn’t a side effect which is hidden by the operation, its all returned as part of the main representation and is traceable.

I see the example you set up is contrived, so it begs the question: is this always a case of sub optimal API design?

207 is an interesting thought, however {json:api} is rather prescriptive in it’s formats and I would suggest you write an extension specification on the WebDAV multi status element for application/json and then project it to application/vnd.api+json with an official {json:api} extension before you proceed on that path. The point of all this is to do things in a standards oriented way. {json:api} doesn’t address transactions or chaining (yet), because the semantics of HTTP are not RPC by nature, and collections of operations passed as a single interaction is bordering very closely on RPC.


#7

Thank you so much for that Michael - much food for thought…

I do take your point on keeping client dumb.
My only defence here is that the client doesn’t have to do any of this management of removing items - it does however have to manage displaying different types.

I would love your feedback on one question…

If, we assume the designs moves to a container and a bunch of links to resources (mitigating this polymorphic item) how does the same scenario play out?

Semantically (at least to me) it doesn’t make sense to apply discount to a product item - the discount is applied to the cart as the discount is dependant on more than one of the items appearing (in a 3 for 2 offer for example). Please offer some further your thoughts on that if your happy to.

On the premise that includes hold the items in the cart, and the presence of one item (a discount) is predicated on the satisfaction of a rule that is negated within a request, how best to indicate that not only has the desired item been removed, but also another item that previously existed in the includes has also been removed.


#8

Sorry for the delay, I had a rather long queue of things to go through and read, and I just haven’t had much chance to get back here.

I’m not sure how it would really effect anything, and why one of those links can’t be ‘discounts’ applied however you want. Assuming you’re sending the cart representation back during each interaction the server would manage the application of any discount, and removal of all changes in the API would be a matter of diff’ing or watching specifically for discount changes on the consumer.

The way to answer the best way to display is a UI/UX question, and to be quite honest while I can answer and give you good broad strokes, I am not the resource you want to consult on UI design.


#9

Very interesting question. Actually your question is beyond of JSON API (even API design). The solution is different depend on decisions on both client and server side. But I think your problem is:

If one operation for a resource also affects other resources,

  1. Should the logic of related resources change happen on server side, or client side?
  2. If 1 is server side, should we notify the client about the change? If so how should we do that?

I recommend you to reconsider the whole design, but let’s assume both above questions are yes first. In your shopping cart example, my solution is to use the operation to modify the “cart” resource other than “cart item” resource. A weird example is using POST /cart/delete-item but not DELETE /cart/items/:id. The benefit is, your API response can always return the latest state of the cart (or order). And every time you use the server state to replace the client state.

The reason is, what really matters is the resource, not the url. This applies to all API design. Even in JSON API, using resource name + id as url is a recommendation but not specification. If you aren’t convinced, think about how you query data from database – you can use different queries to get the same record. The same applies to url and resource in API design.

Another way to notify the client is to send the resource changes, but I prefer to send the whole cart state. Because the client doesn’t need to know how to apply those changes, it just re-render the cart and related resources (like the page refreshes). Even your requirement changes in future – like adding new kind of resources under the cart – the client is not affected.

For comparison, I can give you another cart solution from one of my previous projects. The requirement is alomos the same, and we decided to preload all promitions on the client and let it to manage all cart order changes, because it’s an tablet app, we can consider it’s safer than web browser and we need every action to have instant response. When the client prepared the order, it used POST /orders to submit all related resources to the server. We also do validations on server side to make sure all promotions are used correctly. Although we have slightly logic duplication on both client and server, it’s reasonable in the whole system’s viewpoint.


#10

BTW I think many developers think that RESTful or inspired API designs is to abstract the system operations like CRUD, then make things simpler. It’s wrong. This design can simplify some (maybe most) common case, make our interfaces consistent. But there’re always complex business logics that can’t be emulated as CRUD, and this thinking introduces related problems, like:

  1. A modify operation is better to modify a single resource (let’s ignore batch update this time). If an operation for a resource also makes other resource changes, that’s a “side effect”. and how do we handle side effect?
  2. A resource is better to use /resource-type/unique-identifier to be RESTful, so how do we handle authorization for same operation in different cases?

I think there’s no rule for “one operation is for one kind of resource”. When we design the interface for internal part of the system (i.e. a function), we don’t worry too much about them. Frankly speaking it doesn’t affect our interface design too much. Every real world system has some side effect, no matter what programming language we’re using.

This is also not because of HTTP’s stateless. HTTP is just one kind of web interface for our application, and most of our applications are stateful. Stateless is just a feature in interface level.


#11

Hey David, there’s a lot of stuff in there I agree with, think is good advice which paralleled many of my points. However, I’d caution presenting the /resource_name/:id convention as a solution with respect to the URLs having any semantic meaning. Just because it’s common practice doesn’t make it best practice, or frankly a good idea.

I think you’re second post is touching on the issues of statically defined and coupled APIs vs hypermedia. You might be interested in reading my hypermedia API design guidelines, there may be some helpful stuff in there for you regarding resources not being objects, and HTTP messages not being RPCs.

I would disagree on your final point about statelessness, as it has tremendous beneficial properties to an architecture and shouldn’t be casually disregarded. The easiest benefit to explain is caching, you can never cache in a stateful architecture. Downplaying the positive benefits of statelessness at your interface throws the entire opportunity of leveraging backbone hardware to serve stateless responses without the request ever reaching your services. Ben Greenberg of Comcast had a great talk on a case study of hypermedia in the wild which really demonstrates the importance of statelessness. The tl;dr of it is they couldn’t throw more hardware at the problem, even in the cloud because of usage patterns. Statelessness with hypermedia solved the overload AND greatly reduced the cost.