I\'m having a real hard time conceptually understanding something proper concerning URLs for versioned resources.
Let\'s say I have an application that tracks recipes in
I have a use case where the creation time of each version is crucial. So common questions that the API must answer are:
/recipes/ultimate-thing
?/recipes/ultimate-thing
was the most recent on 2019-01-01?/recipes/ultimate-thing
were created between 2019-01-01 and and 2019-01-31?Number 3. seems to me an obvious case for for query parameters:
-- /recipes/ultimate-thing?versions_since=2019-01-01&versions_until=2019-01-31
It will probably not hurt to use the same approach for 2.:
-- /recipes/ultimate-thing?version=2019-01-01
Then let's be consistent and use the same approach for 1.:
-- /recipes/ultimate-thing?version=latest
From this point of view your option 1) - using query parameters - seems most natural.
I just implemented this pattern late last year, and followed a combination of 2a and 2b:
2c) Nested resources with versions considered subsets
-- /recipes/ultimate-thing/versions/ -> List of available versions of Ultimate Thing
-- /recipes/ultimate-thing/versions/2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing/versions/latest-> Redirect to current working version of Ultimate Thing
Don't think of longer URI's as "subresources" or "attributes", as if a URI identifies a thing. It's much more natural to treat a URI as identifying data, and, since HTTP URI's are hierarchical, longer URI's refer to subsets of data. An item in a collection is data: a subset of all the data in the collection. An "attribute" of a "thing" is data: a subset of all the data about that thing. A "subresource" is data about a "thing" that does not exist independently of its parent, and therefore is a subset of all the data about the parent.
In your case, you don't have to perform too much mental judo to think of recipes/cherry-pie/
as all the data about what a cherry pie is and how to make one, rather than a specific version of the instructions. That includes identity info and other non-version-specific data, but also a link to versions/
, and links to the history and funny videos where someone gets one in the face. The URI recipes/cherry-pie/versions/
is then the subset of all cherry pie data which is versioned; in general, it is nothing more than a collection of links to each URI of the form recipes/cherry-pie/versions/{version}/
. I would also include in versions/
a documented link to recipes/cherry-pie/versions/latest/
, which does a temporary redirect to recipes/cherry-pie/versions/182/
or whatever the latest version is.
This works very naturally for clients. Lots of people trip up on what they see as an explosion of resources, and think the poor client can't possibly keep up. But if you divide your resources along natural user workflow boundaries, you won't have a problem. Some clients will want to browse a set of recipes; some will drill down to a single recipe (regardless of version); some will drill down to want to know how many versions there are, which one is the latest, and will select from among them; some will drill down and retrieve a single version. If you want to show all the versions for a single recipe, or for all recipes, you're probably doing something wrong (in the rare case you're not, design another resource, like recipes/byname/pie/quickview
, which collects all the data and caches it heavily).
Wow. Good question. I too find that one of the biggest challenges of REST is that it is a style, not a strict specification. The style seems not to be well-understood, and there is nothing that enforces it.
Short answer: Style #1: /recipes/cake?version=2
This style seems most appropriate to me. It was only after a quick Google search that I found that query parameters are considered part of the URI. Thus it seems appropriate to put an identifying attribute in the URI.
Styles 2 and 3 seems to violate other REST conventions where path segments represent IDs and sub-resources. Thus the style /recipes/cake/2 looks like the "cake" resource with ID 2. /recipes/cake/version/2 makes "version" appear to be a sub-resource, when in fact it is an attribute of the parent resource.
It all depends on how are you looking at the resource versions. Would these be just used for historical tracking like in wiki or the versions are separate resources. In the first case the EJK answer seems like a good solution. For the second case creating new unique identifier for the resource versions/variations could be another solution. For example:
3) Resources with versions
-- /recipes/ -> List of available recipes versions
-- /recipes/ultimate-thing-version-2 -> Version 2 of Ultimate Thing
-- /recipes/ultimate-thing -> The "offical" version of Ultimate Thing
This is even more prefered solution if the resources are variations of each other:
3) List of recipes
-- /recipes/ -> List of available recipes and variations
-- /recipes/ultimate-thing-with-sugar -> The Ultimate Thing recipe with sugar
-- /recipes/ultimate-thing-with-honey -> The Ultimate Thing recipe with honey