22

I have a REST URI for a list of resources, something like:

http://foo.com/group/users

Each of these users has a sequence number and I want to expose a way to renumber those values for all the users in the collection and make this change available to everyone who accesses the list. Since this is an action on the collection as a whole, I'm not sure how to accomplish this.

I can envision a URL like http://foo.com/group/users?sequence=normalize but neither a PUT nor a POST really makes sense for the whole list, unless I submit the whole collection with the new numbers as the message data.

How can I make an update to an entire collection like this in a RESTful way without having to resend all the updated resources in the collection?

rook
  • 5,880
  • 4
  • 39
  • 51
Nick Gotch
  • 9,167
  • 14
  • 70
  • 97

3 Answers3

26

After the raffian's comment on my initial response, I reworked my answer to be more RESTful...

  • Use the method PATCH

This method is typically designed to update partially the state of a resource. In the case of a list resource, we could send a list with only the elements to update and the identifiers of elements in the list. The following request would be:

PATCH /group/users
[
    { "id": "userId1", "sequence": "newSequenceNumber1" },
    { "id": "userId2", "sequence": "newSequenceNumber2" },
    (...)
]
  • Use the method POST on the list resource

This method is commonly used to add an element in the list managed by the resource. So if you want to leverage it for this action, we need to pass within the request an hint regarding the action to execute. We have the choice to add this either in a dedicated header or within the payload.

With the header approach, you will have something like that:

POST /group/users
X-Action: renumbering
[
    { "id": "userId1", "sequence": "newSequenceNumber1" },
    { "id": "userId2", "sequence": "newSequenceNumber2" },
    (...)
]

With the payload approach, you will have something like that:

POST /group/users
{
    "action": "renumbering",
    "list": {
        [
            { "id": "userId1", "sequence": "newSequenceNumber1" },
            { "id": "userId2", "sequence": "newSequenceNumber2" },
            (...)
        ]
    }
}

Hope it helps you, Thierry

Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • This seems like the right direction, but how do I handle if the request doesn't include all the current users in the list? For example: User A gets the list which has 50 users in it, User B adds a new user to the list, then A sends the renumbered 50 in a PATCH but it doesn't account for that new user (or vice-versa if a user was deleted.) I'm concerned in this case we'd end up with two duplicate numbers; is there any way to prevent that? – Nick Gotch Feb 23 '15 at 18:28
  • 4
    I think you should consider to leverage the HTTP headers `ETag` and `If-Match` to implement optimitic locking with REST. This link could interest you: https://looselyconnected.wordpress.com/2010/03/25/the-http-etag-header-and-optimistic-locking-in-rest/. Hope it helps you. – Thierry Templier Feb 24 '15 at 13:27
7

Semantically speaking, the HTTP PATCH method is the right way to go. This is also described in the currently chosen answer.

PATCH /group/users

[
    { "id": "userId1", "sequence": "newSequenceNumber1" },
    { "id": "userId2", "sequence": "newSequenceNumber2" },
    ...
]

However, the second method described in the chosen answer is not restful, because you invented new verbs inside a POST request. This is SOAP, not REST.

Community
  • 1
  • 1
RayLuo
  • 17,257
  • 6
  • 88
  • 73
  • rest is not the best style, actually, sometimes it reduces expressiveness and productivity. sometimes the best is to create new methods (verbs). take a look at Google Cloud APIs and see how they invent new methods every time they need them. – mhrsalehi Jul 28 '22 at 12:49
3

You can use both PATCH and POST on the URIs. I'd use PATCH if I were you. It's the best solution for bulk updates.

inf3rno
  • 24,976
  • 11
  • 115
  • 197