Se7enDays
Se7enDays

Reputation: 2593

Good REST API design for operations on resource sets

With REST it is pretty clear how to operate on resources, e.g.

PUT /users/{userId} - updates the user with userId
GET /users/{userId} - reads the user with userId

Similarly for resource sets

POST /users - creates a new user
GET /users/{userId}/books - reads list of books from a user
GET /users/{userId}/books?filter=x - reads list of books from a user with specific filter

What if I want to develop more elaborate operations on resource sets, e.g.

  1. with the request body, add a list of books to the existing list and accepting duplicates (basically concatenating the list)
POST /users/{userId}/books 
or PUT /users/{userId}/books 
or PATCH?
or POST /users/{userId}/books/concatenate
  1. with the request body, add a list of books to the existing list but no duplicates (basically merging the list)
POST /users/{userId}/books 
or PUT /users/{userId}/books 
or PATCH?
or POST /users/{userId}/books/merge
  1. also for deleting parts of resource sets: with the request body, delete a list of books from the existing list that have a certain property
POST /users/{userId}/books/delete?category=x 
or DELETE /users/{userId}/books?category=x
  1. or deleting all resources in a resource set:
POST /users/{userId}/books/delete_all 
or DELETE /users/{userId}/books

Would be thankful for some hints or guidelines

Upvotes: 0

Views: 783

Answers (1)

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57257

"Resource sets", from the point of view of REST, are a fiction. There are only resources. As far as a general-purpose HTTP component is concerned, there is _no relation implied by the following URI:

/users
/users/{userId}
/users/{userId}/books
/users/{userId}/books?filter=x
/users/{userId}/books/concatenate

They are completely independent of one another; for instance, DELETE /users does not imply anything about the other resources.

We human beings tend to assign identifiers in patterns that make sense, but the machines don't care.

with the request body, add a list of books to the existing list and accepting duplicates (basically concatenating the list)

PUT and PATCH have remote authoring semantics; they act like you would expect if you were trying to edit a copy of a file on the server. You GET a copy of the current representation of the resource, make edits to your local copy, and then request that the server change its copy to match your copy. With PUT, you send a complete copy of your representation of the resource; with PATCH, you send a patch-document that describes the changes you made.

It's okay to use POST; HTML got along just fine using nothing but GET and POST, and the web took over the world.

You don't need a separate resource for POST; you can use one if you like, but it isn't necessary to do so.

with the request body, add a list of books to the existing list but no duplicates (basically merging the list)

Not really any different; what we agree upon in HTTP is the semantics of the request and response messages. What the server chooses to do is an implementation concern. See Fielding 2002.

So if I send to you a representation of a list with duplicate entries, and you strip out the duplicates, that's "fine"; you just need to exercise some care with your responses to ensure that you don't imply that you accepted the requested representation as is.

With PATCH, it's a bit fuzzy, in that the RFC describes all or nothing semantics, but based on the language used it is reasonable to infer that the implementation is restricted as well.

also for deleting parts of resource sets: with the request body, delete a list of books from the existing list that have a certain property

Give RFC 7231 a careful read: DELETE doesn't quite mean what your examples hint at. DELETE breaks the associate between a key (the target uri) and a value (the resource representations), but that doesn't necessarily mean "and also garbage collect the representation".

The same idea expressed another way -- suppose I GET /list-of-books from the server, and the returned representation is a list of three books. In the case where I want that resource to instead return a representation of an empty list, DELETE is the wrong tool. DELETE tells the server that I want future calls to GET /list-of-books to return 404 Not Found or possibly 410 Gone. If what I really want is a 200 OK with an empty list, then I need to PUT/PATCH/POST/etc. the resource.

deleting all resources in a resource set

Same problem as before.

With REST it is pretty clear how to operate on resources

This is the problem - it is NOT clear how to operate on resources. The web is cluttered with literature that makes a complete hash of it (we use REST to fetch documents that mangle the lessons of REST -- fabulous irony).

REST includes a uniform interface as a constraint. In HTTP, that interface is effectively a document store. PUT and PATCH just edit document contents - which is perfectly satisfactory if your domain is anemic or declarative. For anything else where we don't have standardized semantics, we use POST.

See Jim Webber, 2011: "You have to learn how to use HTTP to trigger business activity as a side effect of moving documents around the network."

Upvotes: 1

Related Questions