Search code examples
restnosqlsubdocument

How to POST, PATCH and DELETE items from a subdocument array using REST


Let's say we had the following User document:

{
  "_id": "1",
  "firstName": "Joe",
  "hobbies": [
     "_id": "1",
     "name": "music",
     "talented": true
   ],
}

So let's say we wanted to POST, PATCH or DELETE one of Joey's hobbies. How should we proceed using a rest api?

I thought about doing something like this:

POST - /users/:id/hobbies


PATCH - /users/:id/hobbies/:id


DELETE - /users/:id/hobbies/:id

This seems pretty semantic and easy to read, but on the other hand, it feels wrong to append the subdocument name as a resource to the route, since it is a subdocument and belongs to the main User document.

So, the other way I thought is to simply make a patch to the main User document:

PATCH - /users/:id/

Which rest route structure is correct for achieving these tasks?


Solution

  • Let's say we had the following User document How should we proceed using a rest api?

    By treating the document like a document: make the edits locally, and send the result back to the server.

    Assuming that this document was available at /users/1/, we would just send back a representation with the hobby removed...

    PUT /users/1/
    
    {
      "_id": "1",
      "firstName": "Joe",
      "hobbies": [
       ],
    }
    

    Using PATCH instead of PUT is fine (provided we are sending a patch document as the message-body). Technically, you could also use POST, but POST doesn't really offer you any advantages here.

    /users/:id/hobbies
    /users/:id/hobbies/:id
    

    The problem with using identifiers like this, is that generic clients won't recognize that they have anything to do with /users/:id/ - so even though you send a message to delete a hobby, the client's locally cached copy won't get updated (because it uses a different key).

    Now, if your resources were designed using links

    {
      "_id": "1",
      "firstName": "Joe",
      "hobbies": [ { "href": "/users/1/hobbies/4" } ]
    }
    

    Then we would still use /users/1/ to add/remove hobbies from the collection, but if we wanted to modify the representation of the hobby itself, then we would be sending messages using /users/1/hobbies/4.

    If the hobbies collection itself were a link...

    {
      "_id": "1",
      "firstName": "Joe",
      "hobbies": "/users/1/hobbies"
    }
    

    Then we would add/remove hobbies by sending messages to /users/1/hobbies.

    It may help to think about web pages - the HTML representation of a web page will often include links to images or scripts that are fetched and cached separately from the page itself. If we wanted to edit the HTML, we would send the request using the identifier for the page, if we wanted to change the script, we would send the identifier for the script.