Ember + Nested RESTful-ish API + Pain


#1

I’m attempting to do something that, to me, has been simple with every other client-side framework I’ve worked with in the past. So far, it seems like Ember is incapable of handling this case without a massive amount of extra work. Here’s a general outline of the problem:

My API looks something like this (but with different model names):

/users
/users/:user_id
/users/:user_id/widgets
/users/:user_id/widgets/:widget_id

This is extremely common, extremely usable API design.

Pretty straightforward. In my Ember app, I’d like to define a route (probably just /users/:user_id) that displays both information about the user (that is returned from the API at /users/:user_id), as well as information about each widget (returned from the API at /users/:user_id/widgets). Ideally, my page doesn’t render until all of this data is loaded, and it should load very quickly because I’d ideally be making both required API calls simultaneously.

My templates, logically, would look something like this:

<h1>User {{name}}</h1>

<h2>Widgets</h2>

<ul>
  {{#each widget in widgets}}
    <li>{{widget.name}}</li>
  {{/each}}
</ul>

In fact if I had many other nested resources just under the /users/:user_id level, I should be able to list all sorts of data about those items without taking a significant performance hit, because I can make all of the API requests simultaneously (because I know the user ID).

From what I’m reading, ember-data does not have support for nested resources, so that’s not a possibility (it’s not a weird Rails API where I can just call /widgets?user_id=:user_id). Looking beyond that, it seems like every time a question like this comes up, a completely different and typically convoluted solution is proposed. The rest of Ember looks awesome, but the seemingly giant complexity of this simple task in Ember is a blocker to me adopting the framework, right now, especially when other frameworks like Angular, for example, have mature http frameworks like restangular that understand how RESTful APIs tend to be written, and already have support for niceties like conditional GETs.

Any suggestions or discussion around this would be greatly appreciated. Is there already a mature library that works with Ember that supports this?

EDIT

I spent another few hours working on this. I think that the way that Ember.RSVP works and the way that Ember.Route#model waits for promises should really be exposed much more in documentation and general discussion around ember. Once I realized I could just construct a hash of $.getJSON calls with Ember.RSVP::hash, a lot of my problems seem much easier to deal with.


#2

It looks like you can use relative paths when you use “links” in the json from the server.

"person": {
  "name": "John Smith",
  "links": {
    "children": "./children",
    "jobs": "./jobs"
  }
} 

If you can’t/won’t change the format of the json from the server you could always extend the RestAdapter to modify the format of the json when it arrives at the client.


#3

@clem I am totally with you. I’ve been experiencing the same pain. The continuing inability of the Ember team to solve this problem seriously makes me question whether Ember is a viable framework. Client side frameworks should NEVER impose their philosophies on the resources that provide their data.


#4

@clem Do you have an example of how you handled that? I would be very interested to see it.


#5

@dougrohde Briefly, I’m just using a general Model class that wraps $.getJSON and some caching behavior. User and Widget extend this class, but there’s not has-many-belongs-to relationship set up, because I don’t actually need it. If I needed both user and widgets in a route, I’d do something like this, now:

UserIndexRoute = Ember.Route.extend({
  model: function(params) {
    var user = User.find(params.id);

    return Ember.RSVP.hash({
      user   : user,
      widgets: Widget.all(user)
    });
  }
});

My Model that Widget and User extend is smart enough to turn Widget.all(user) into a $.getJSON to /users/1/widgets (given a user with ID 1).

Honestly, now that I’ve decided not to consider ember-data and build my own solution, I don’t feel quite as worried. It wouldn’t take much to implement something that looked like this:

UserIndexRoute = Ember.Route.extend({
  model: function(params) {
    return User.find(params.id).with('widgets');
  }
});

If I wanted both user information and the user’s widget list.