Ember Data: working with nested API resources

Posted by Kevin Soltysiak on October 19, 2015 Topics: ember.js and ember-data

If you are using Ember.js, chances are you are also using Ember Data. While it is not the only option for communicating with your backend, it's the golden path: it's included out of the box when creating a new project with ember-cli, mentioned in the guides, and most resources you'll find assumes you're using Ember Data.

ED has been stable for quite some time now, but there is still a few use cases it does not cover out of the box. I'll be exploring one of them in this article: handling APIs with nested resources (eg. JSON APIs with URLs such as /posts/1/comments).

Before I go further: I have dealt recently with the situations mentioned below. As such, code samples were extracted from real-life projects, but edited for the purpose of this article, and may not work exactly as is: they're here mainly to give some substance to the concepts explored.

URL Templates to the rescue

When dealing with such APIs, I used to handroll my own solution, subclassing and overriding buildUrl in my adapters everywhere it was needed. But a few weeks ago, I found the addon ember-data-url-templates.

This addon allows you to specify a URL template that may include dynamic segments for each adapter operation. Then, you need to define methods used to resolve those dynamic segments, and that's it! Here is a basic example, taken from the project's README:

This example shows how to use the model relationships' to build the URL. Pretty neat, right? However, we cannot always rely on the relationships to build the URL, and that's where things gets interesting.

Building URLs using outside data

In the example above, we see that the methods in urlSegments are given a few arguments: type, id, snapshot, and query. Now, let's say we have a post model, and we want to fetch all its comments, and that unlike our example, we can only fetch them through /posts/{postId}/comments.

In this case, there is no id and snapshot that we can use to extract the postId. The data we need is outside of our reach, so we need to find a way to "pass" it to our adapter.

For this purpose, I decided to use a service object that I call adapterContext. This service is responsible for holding contextual data needed by the adapter, and could be like this:

This service would be injected in your routes, controllers, adapters, and probably components too. Then, you could use it like this:

And that should work as you expect. Using setContext instead of directly setting the properties on the service ensures that the context data set with a previous call is not kept around, allowing you to not call resetContext every time.

Resource that may or may not be nested

Now, what if we're dealing with resources that may or not be nested, and nested under different types of parent resources? One such case could be users: you can list them directly (/users), or only want to find users belonging to an organization (/organizations/1/users/), or to a company (/companies/1/users).

It actually is pretty straightforward: you can use computed properties when defining your url templates! You can then check the adapterContext to see which property is present, and choose which URL to use based on that:

What's next

ember-data-url-templates is pretty useful and built on solid foundations: URL templates are described in RFC 6570 and the addon adheres closely to it. It may also land in Ember Data itself one day too, if proven robust and useful enough.

Later this week, I'll explore how Ember Data can leverage ember-data-url-templates to use custom API endpoints (such as /posts/starred), so stay tuned.

As always, I hope this article was useful and clear enough. Feel free to leave a comment, or get in touch via email or twitter if you have any comment or feedback to make.