0

I'm trying to build an web API that closely represents a library of books. I currently have the following routes.

  • GET /api/books
  • GET /api/books/:bookID
  • GET /api/libraries
  • POST /api/libraries
    • This route currently accepts a books parameter in the body (with the body represented as an object), that should be an array of bookIDs.
  • GET /api/libraries/:libraryID
  • DELETE /api/libraries/:libraryID

What I'm trying to decide is how to allow users to add/delete books to an existing library.

Option 1:

  • POST /api/libraries/:libraryID/books/:bookID
    • No body will be accepted in this route
  • DELETE /api/libraries/:libraryID/books/:bookID

Option 2:

  • POST /api/libraries/:libraryID/books
    • Body will be an array of bookIDs represented as an array.
  • DELETE /api/libraries/:libraryID/books
    • Body will be an array of bookIDs represented as an array.

Option 3:

Some combination of 2 & 3, OR something completely different that I haven't thought of yet.


What is the most common method to handle this for a REST API? In the future I might also consider PUT /api/libraries/:libraryID, but that would change the entire books array, and replace it, as opposed to just adding or deleting a specific book(s).

2
  • I would recommend removing the /api prefix from all of these URLs — it adds nothing and reinforces misconceptions that APIs are something different to websites. Commented Jul 27, 2019 at 9:52
  • /api./AddBookToLibrary?libraryId=x&bookId=y is impossible to misunderstand
    – Ewan
    Commented May 21, 2023 at 14:48

1 Answer 1

1

Heuristic: think about how you would do it with a web site. That alone will tell you a lot about how it would be done as an API.

In the future I might also consider PUT /api/libraries/:libraryID, but that would change the entire books array, and replace it, as opposed to just adding or deleting a specific book(s).

In REST, this it the right basic idea. Remote authoring is done by retrieving a representation of the resource, making local edits, and then sending back to the server a request that the server change its copy to match the local copy.

If the total representation is small, then sending the entire thing with PUT is fine. A valid alternative is to describe the changes in a patch document, and send that using HTTP Patch.

On the web, the remote authoring idioms aren't nearly as common as the form idioms. Download a form that allows you to describe some action, fill in the form details, submit the form back to the target resource via POST. Form submissions have been changing the web for 25 years now, so it is a valid choice.

We should prefer to use /api/libraries/:libraryID as the form target, because that's the key we want to invalidate in the local cache. HTTP capable caches will have the correct semantics baked into them, providing that we ourselves follow the specification.

So for your case, I'd probably have a form for adding a book, perhaps at /api/addBook?library=:libraryID, and another form for deleting a book, perhaps at /api/deleteBook?library=:libraryID. Each of these resources would provide a representation of the form, with the appropriate fields to be filled in by the client, and meta data telling the agent to submit to /api/libraries/:libraryID.

If you look carefully, this suggests that you might have a lot of different forms all submitting to the same resource. That's right - all of the actions that should change the representation of /api/libraries/:libraryID should be implemented as forms that POST to the same identifier. The benefit is free caching; the trade off is that we have to prepare our endpoint for many different kinds of form submissions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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