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
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:
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
In this case, there is no
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 (
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:
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.