9

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 one Bar. The reason why I don't treat Bar as a subresource of Foo is because the same Bar instance can be shared between mutiple Foos. 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 serialized Foo), which makes the Foo 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 the fooandbar 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?

  • What's the term for the relationship between Foo and Bar? Could you say that Bar is a parent of Foo? – Nathan Merrill Nov 10 '16 at 12:59
  • A Bar cannot exist without being associated to a Foo. However, as I wrote above, it's possible that multiple Foos share the same Bar. It should be possible to create a Foo without a Bar associated, so I don't think Bar should be treated as parent. – ceran Nov 10 '16 at 13:03
  • 1
    I think you are experiencing some of the problems I have had by translating directly domain model relationships into URIs and equating resources to domain entities. It might interest REST APIs must be hypertext-driven. Special attention to the 4th point – Laiv Nov 10 '16 at 13:24
5

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 a uri 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 Nov 10 '16 at 13:46
  • The drill down isn't really a rest feature, so there are no standards (at least that I know of). – Qwerky Nov 10 '16 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 Nov 11 '16 at 9:59
4

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 each Bar is quite huge in memory (around 3x-4x the size of Foo), I don't want to return a Bar if a client does not request it explicitly. – ceran Nov 10 '16 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 speed – Nathan Merrill Nov 10 '16 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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