Adding query parameters to a hasMany relationship

Your hasMany query returns a ton of results that you may decide to filter or paginate. Comments on a popular blog post (with a nested URL such as /api/posts/1/comments?page=1) is a great example.

Or perhaps your backend API requires an additional parameter for some specific operation.

Unfortunately, Ember Data does not support passing query parameters to relationships. How do we deal with this situation?

Use ember-data-has-many-query

This add-on allows us to directly add query params to hasMany (and belongsTo) associations. By simply including two mixins we can start querying associations from our model:

post.query('comments', { page: 1 });

I recommend using this add-on! For more info see the documentation.

If you have a more specific use-case (or want to understand what happens behind the scenes) we’ll attempt a basic implementation without the help of an add-on.

Overriding the adapter

As we saw earlier, URL or location customizations are done in an adapter.

But what is the adequate method to override? In the case of hasMany, we should be using findHasMany which is available in RESTAdapter and related adapters like JSONAPIAdapter.

That is, if we want to dynamically provide a page number for the Post’s comments, we have to override findHasMany in the PostAdapter. We retrieve a previously set property (query-params) with the desired values, and lastly turn this object into a query parameter string:

// app/adapters/post.js

export default DS.JSONAPIAdapter.extend({

  findHasMany(store, snapshot, url, relationship) {

    const id =,
      type = snapshot.modelName;

    url = this.urlPrefix(url, this.buildURL(type, id, null, 'findHasMany'));

    if (relationship.type == "comment" && snapshot.record.get('query-params')) {
      const qp = $.param(snapshot.record.get('query-params'));
      url += "?" + qp;

    return this.ajax(url, 'GET');


Before accessing the hasMany association again, we set the query-params property and trigger a reload. (Alternatively the reload can be done through fine-grained cache control).

Here’s a usage example:

// in some route

paginate(page) {
  const post = this.currentModel;
  post.set('query-params', { page: page });

For a working sample app, see branch hasmany-params of this sample blog app.

Which means that a setting like post.set('query-params', { from: '2014-01-01', to: '2014-01-10' }) will result in a /api/posts/1/comments?from=2014-01-01&to=2014-01-10 call to the backend.

Enjoyed this article? Don't miss my next one!

Leave me your e-mail for content that will help you master Ember:

Do you want to master Ember fast?

Leave me your e-mail for helpful updates delivered straight to your inbox.

(A few e-mails per month. No BS. Unsubscribe anytime!)