1

I have an API that has a POST method to create a new Product. The creation of the new Product is handled by POSTing to a ProductService. Now I want to be able to instruct the API when creating the new Product to also add the Product to each Store.

What is the best approach to achieving this in a RESTful way? If I POST to an endpoint e.g. /api/products is it appropriate to add a query parameter /api/products?addToAllStores=true?

1

There's nothing wrong in identifying a subset of resources through the query string and add|update resources to the subset.

However, in this particular case, the URI here raises some doubts

/api/products?addToAllStores=true

On one side /api/products identifies a specific set of resources Products, on the other side addToAllStores identifies (somehow) another different set of resources and also denotes operability (something we usually delegate to the HTTP method and to the semantics of the API). Altogether, makes me wonder if it would not be better placed in the payload. For clarity.

Then, reading @VoiceOfUnreason' answer

How would you do it on a web site? There would be a form that the client can fill out and submit to tell you to create the product. If you also wanted to give the client an option to add the product to the store, then you might add a checkbox to the form.

Emphasis of mine

Making addToAllStores part of the message instead of the identifier (URI) would make the API less ambiguous. And practical I would dare to say.

For example, say we want to add products to a smaller subset of stores. We would have to add something like addToStores=x,y,z to the URI. That would make us have 2 different parameters with different types, handled in different ways. Another doubt involves single stores. If there's addToAllStores, well there must be a way to inform a single store. If that way is in the payload, a consistent API would send addToAllStores in the payload too.

Finally (and this is an opinion of mine), if we need to add different Products in different Stores in the same transaction, we have to add a new parameter or change the payload to support product - stores relationships. More likely the later because it would be complicated to do in the former.

Say then, we move this "relationship" to the payload as we had filled up the web form suggested by @VoiceOfUnreason. The payload could be similar to

{    
  "products":[
     { "name":"x", code:"x", stores:["*"] },
     { "name":"y", code:"y", stores:["A","B"] },
     { "name":"z", code:"z", stores:["C"] },
     { "name":"w", code:"w", stores:[] },
    ] 
}

It's just an example but it shows how we could reach the same goal with a single payload scheme, without having to delegate intentionality to the URI with different query parameters -- a place we (devs) usually don't look for loads parameters. Additionally, the input is packed in such a way it's portable to other protocols that might not rely on URIs to carry information.

Anyways, again, there's nothing wrong in using query strings + POST. It's a matter of convenience or pragmatism. If you have identified reasons to send addToAllStores through the query string and it meets your needs, that's fine too.

  • Interestingly (to me at least) I have been busy on the other side of the equation; creating a new store and adding products to it. The outcome of that process is very similar to your last example so addAllProducts is a convenience checkbox on the form but I provide a list of products on the same form that the user can select. This is then propagated as a List<ProductId> as part of the model. Checking the checkbox just adds all the products to the list. – David Clarke Apr 3 at 19:48
  • 1
    That's sound (IMO) more dev-friendly. Now, as shown in the example, you could implement a massive load of stores/products with different relationships in the very same scheme and API (why not). With the parameter in the query string It would not be so evident or intuitive to do. – Laiv Apr 3 at 19:58
2

What is the best approach to achieving this in a RESTful way?

How would you do it on a web site?

There would be a form that the client can fill out and submit to tell you to create the product. If you also wanted to give the client an option to add the product to the store, then you might add a checkbox to the form. The default value of the checkbox would be whatever state gives you backwards compatible behavior -- guessing from your description, the box should be unchecked, and clients would have to check the box to "opt in" to the storage.

And of course, when submitted, the form would go to the same place whether or not the box was checked. The server would look at the payload and figure out what to do.

A different RESTful way to do it might be to start with a form that allows the user to create the product, and then after the form is created give them another form that lets them add it to the store.

Another way you might do it is to have two different forms; one for creating the product, another for creating the product and adding it to the store. You would ask the client which option they want, and direct them to the appropriate form.

And of those approaches are fine for a REST API as well; you are doing the same thing, just in such a way that a program can understand the semantics of the web site.

I would normally have all of the form submissions using the same target-uri, to pick up the right cache invalidation semantics for "free". But doing it that way requires that the server determine the semantics of the request from the request-body. Your circumstances may require different trade offs than mine.

  • The form examples don't resonate with me - when creating a form it should be designed from the user experience, not from the easiest implementation. Why should a user have to answer an extra question just to determine which form to display? Those choices can significantly erode the usability of a site. Thinking of the website as the presentation layer then yes I agree, a checkbox binds to a boolean in the model. But sending that boolean to the API as part of the model doesn't make sense; it's intended to adjust the behaviour and isn't part of the domain. – David Clarke Apr 3 at 19:35
1

It would make sense to have it as a query parameter as you have done. POST can function differently to other request types however this data is only meaningful in a POST so putting a boolean on the Product object would not help when a GET is done on it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.