I'm in the progress of creating a REST API and currently, I'm encountering the following problem:
Foo
is the first resource. CRUD operations can be applied via the/foo/
URI.Bar
is the second resource. CRUD operations can be applied via the/bar/
URI.- Every
Foo
is associated with zero or oneBar
. The reason why I don't treatBar
as a subresource ofFoo
is because the sameBar
instance can be shared between mutipleFoo
s. So I figured it's better to access it via an independent URI instead of/foo/[id]/bar
.
My problem is that in a significant amount of cases, clients which ask for a Foo
instance are also interested in the associated Bar
instance. Currently, this means that they have to perform two queries instead of one. I want to introduce a way that allows to get both objects with one single query, but I don't know how to model the API for doing that. What I came up with so far:
- I could introduce a query parameter similar to this:
/foo/[id]?include_bar=true
. The problem with this approach is that the resource representation (e.g. the JSON structure) of the response would need to look different (e.g. a container such as{ foo: ..., bar: ... }
instead of just a serializedFoo
), which makes theFoo
resource endpoint "heterogeneous". I don't think that's a good thing. When querying/foo
, clients should always get the same resource representation (structure), regardless of query parameters. - Another idea is to introduce a new read-only endpoint, e.g.
/fooandbar/[foo-id]
. In this case, it's no problem to return a representation like{ foo: ..., bar: ... }
, because then it's just the "official" representation of thefooandbar
resource. However, I don't know if such a helper endpoint is really RESTful (this is why I wrote "can" in the title of the question. Of course it's technically possible, but I don't know if it's a good idea).
What do you think? Are there any other possibilities?
2 Answers 2
A level 3 REST API would return you a Foo
and also a link indicating the related Bar
.
GET /foo/123
<foo id="123">
..foo stuff..
<link rel="bar" uri="/bar/456"/>
</foo>
You could then add a "drill down" feature to your API which allows the navigation of links;
GET /foo/123?drilldown=bar
<foo id="123">
..foo stuff..
<link rel="bar" uri="/bar/456">
<bar id="456">
..bar stuff...
</bar>
</link>
</foo>
The drill down feature would sit in front of the APIs and intercept responses. It would make the drill down calls and fill in the details before handing the response back to the caller.
This is a pretty common thing in level 3 REST as it much reduces client/server chattiness over slow http. The company I work for produces a level 3 REST API with exactly this feature.
Update: For what its worth, here's how it might look in JSON. This is how our API would structure it. Note that you can nest your drill downs to pull links of links etc.
GET /foo/123?drilldown=bar
{
"self": {
"type": "thing.foo",
"uri": "/foo/123=?drilldown=bar",
"href": "http://localhost/api/foo/123?drilldown=bar"
},
"links": [
{
"rel": "bar",
"rev": "foo",
"type": "thing.bar",
"uri": "/bar/456",
"href": "http://localhost/api/bar/456"
}
],
"_bar": [
{
"self": {
"type": "thing.bar",
"uri": "/bar/456",
"href": "http://localhost/api/bar/456"
},
"links": [
{
..other link..
},
{
..other link..
}
]
}
]
}
-
Interesting, I'm already using hypermedia links/controls to remove the tight coupling to URIs, but I haven't thought about the "drill down" idea which seems very promising. How could a JSON representation look like?At the moment, each JSON representation of my resources contains a
links
array, each entry is a link object with arel
and auri
field (similar to your xml example). Should I just add a third field to each link object (e.g.data
)? Is there any standard?ceran– ceran11/10/2016 13:46:13Commented Nov 10, 2016 at 13:46 -
The drill down isn't really a rest feature, so there are no standards (at least that I know of).Qwerky– Qwerky11/10/2016 13:53:51Commented Nov 10, 2016 at 13:53
-
There are some proposed standards, such as stateless.co/hal_specification.html which I'm using in our applications. It's very close to your example.Pete Kirkham– Pete Kirkham11/11/2016 09:59:36Commented Nov 11, 2016 at 9:59
If 95% of all queries want Foo
as well as Bar
, then simply return it inside of the Foo
object when you request a Foo
. Simply add a property bar
(or some other term for the relationship), and put the Bar
object there. If the relationship doesn't exist, then use null.
I think you're overthinking this :)
-
I shouldn't have come up with that number (95%), it was a mistake, sorry. What I wanted to say was that a large part of requests is interested in both resources at the same time. But there still is a relevant number of requests that are only interested in
Foo
, and since eachBar
is quite huge in memory (around 3x-4x the size ofFoo
), I don't want to return aBar
if a client does not request it explicitly.ceran– ceran11/10/2016 13:19:21Commented Nov 10, 2016 at 13:19 -
How big are we talking? I doubt that it's going to make that much of a difference in transfer time, and I prefer a clean API over speedNathan Merrill– Nathan Merrill11/10/2016 13:26:30Commented Nov 10, 2016 at 13:26
Bar
cannot exist without being associated to aFoo
. However, as I wrote above, it's possible that multipleFoo
s share the sameBar
. It should be possible to create aFoo
without aBar
associated, so I don't thinkBar
should be treated as parent.