0

Let's suppose I have a N-N association between products and categories, and I want to create endpoints to add and remove categories to a product resource. My initial thoughs were:

  1. Creating endpoints like these:
/products/{id}/addcategory/{categoryId}

/products/{id}/removecategory/{categoryId}
  1. Using PUT verb.

I'd like to know what is the appropriate path structure and HTTP verb in such a case.

Nelio Alves
  • 1,231
  • 13
  • 34
  • 2
    addCategory/removeCategory don't seem REST like to me. I would use PUT/DELETE on /products/{id}/categories/{categoryId} – MarianP Jul 24 '20 at 21:57
  • Makes perfect sense. Thank you! – Nelio Alves Jul 24 '20 at 22:04
  • I've added more details to my [answer](https://stackoverflow.com/a/63082111/1426227) regarding the `PUT` usage. – cassiomolin Jul 24 '20 at 22:31
  • My concern about POST is two fold: (1) we are not "creating" a resource (the association isn't a resource); and (2) we want the operation of adding a category to be idempotent, and POST is not idempotent. – Nelio Alves Jul 24 '20 at 22:35
  • 1
    The association can be seen as a resource. According to Fielding, the guy who defined the REST architectural style, [_"any information that can be named can be a resource"_](https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm#sec_5_2_1_1). If [idempotency](https://stackoverflow.com/a/45019073/1426227) is a concern, then you can use `PUT`. – cassiomolin Jul 24 '20 at 22:39

1 Answers1

2

In fact, you should use HTTP methods indicate the semantics of the request and use representations to manipulate the state of the resources on the server.

To add a given category to a product, you could perform a POST request to /products/{productId}/categories, where the request payload contains a representation of the reference of the category being added. For example:

POST /products/foo/categories HTTP/1.1
Host: example.org
Content-Type: application/json

{ 
  "categoryId": "bar"
}

The response of a successful request could be like:

HTTP/1.1 201 Created
Location: /products/foo/categories/bar

If idempotency is a concern, you can use PUT to create a resource, as long as the resource identifier is provided by the client. This answer I put together a while ago clarifies this. Then your request would be like:

PUT /products/foo/categories/bar HTTP/1.1
Host: example.org
Content-Type: application/json

And the successful response would be much the same as the one shown above.


To remove a given category from a given product, you could use a DELETE request to /products/{productId}/categories/{categoryId}:

DELETE /products/foo/categories/bar HTTP/1.1
Host: example.org

And the response of a successful request could be like:

HTTP/1.1 204 No content
cassiomolin
  • 124,154
  • 35
  • 280
  • 359
  • 1
    Your PUT sample does use the POST operation. A further thing to note maybe is, that REST isn't ideal for 1:1 mappings of domain objects onto resources. While associations can be represented through links, resources should contain more information than domain objects provide (and may even contain data present in other entities or even external data), i.e hypermedia controls such as link relations, form elements and stuff that allow a client to interact with the service further. – Roman Vottner Jul 25 '20 at 12:44
  • 1
    @RomanVottner As always, thanks for your valuable insights. Take care and stay safe in these unprecedented times. – cassiomolin Jul 26 '20 at 20:26
  • I just received my negative attest today after showing most of the common symptoms, but "luckily" I just seem to have a regular flu. Haven't seen you here in a while also, hope everything is fine on your side as well – Roman Vottner Jul 26 '20 at 21:41
  • @RomanVottner I'm glad to know it's nothing besides a flu. Work has never been so busy, but I'm still around :) – cassiomolin Jul 28 '20 at 08:50