0

I'm trying to get Ember Data's JSONAPIAdapter to work with nested resources. For the server part django-rest-framework-json-api is used.

My (simplified) ember models:

case.js

export default Model.extend({
  firstName: attr('string'),
  lastName: attr('string'),
  comments: hasMany('comment'),
})

comment.js

export default Model.extend({
  text: attr('string'),
  case: belongsTo('case'),
})

The server's response for /api/v1/cases/4 looks like this:

{
  "data": [
    {
      "type": "cases",
      "id": "4",
      "attributes": {
         "first-name": "Hans",
         "last-name": "Peter",
      },
      "relationships": {
        "comments": {
          "meta": {
            "count": 1
          },
          "data": [
            {
              "type": "comments",
              "id": "5"
            }
          ],
          "links": {
            "related": "http://localhost:8000/api/v1/cases/4/comments"
          }
        }
      }
    }
  ]
}

Now, if i understand Ember Data and the JSON-API spec correctly, ember should request /api/v1/cases/4/comments when i reference the comments. Instead, it requests /api/v1/comments/5, which obviously returns a 404.

My questions in summary:

  • Does the server response comply to the JSON-API spec?
  • How do i get ember to respect the nested route?

I'm using ember v2.8.

Bonus question: I face the same problem for creating a new comment - how do i get ember to POST to /case/4/comments instead of /comments?

4
  • Yes this works and your json looks correct how do you "reference the comments"? Also my posts are to the comments resources directly. Commented Oct 26, 2016 at 17:01
  • Currently, i just access model.comments in a template. According to the feedback from the ember community slack on this issue there are some subtleties related to how one accesses the resource - do you know more about this? Commented Oct 26, 2016 at 17:18
  • Assuming your model is an instance of "case" this looks correct. I have done this using firebase and loopback. Not sure about the "subtleties". I will add an answer with a json sample and a code sample if you like. Commented Oct 26, 2016 at 17:30
  • That would definitely be interesting. I will also try to reproduce the issue in a minimal setup. Commented Oct 26, 2016 at 19:31

2 Answers 2

1

The JSON API spec does not enforce any specific URL pattern, so what you're trying to do is compliant. However, I find that working with a flat URL structure is easier with Ember Data, though there is a workaround.

You'll want to look at the ember-data-url-templates addon and add some logic from it to your model's adapter.

With that addon, here is what you can do with app/adapters/comment.js:

import ApplicationAdapter from './application';
import UrlTemplates from 'ember-data-url-templates';

export default ApplicationAdapter.extend(UrlTemplates, {
  namespace: 'api/v1', // You may or may not need this namespace setting:
                       // I'm a little rusty in this area :)

  urlTemplate: '{+host}/case/{caseId}/comments{/id}',

  urlSegments: {
    contentId: function(type, id, snapshot/*, query */) {
      return snapshot.belongsTo('case', { id: true });
    }
  }
});

Unless there is something else that the addon allows to get around this, I believe that this then locks you into that URL structure for comments across your entire app. So definitely weigh that tradeoff before deciding to go down this route.

Sign up to request clarification or add additional context in comments.

1 Comment

Since ember-data currently doesn't support following the related links for POST or PATCH requests, this seems to be the way to go if flattening URLs is not an option - thanks!
1

Yes this works it should be setup as follows

models/client.js

export default DS.Model.extend({
    name: DS.attr('string'),
    telno: DS.attr('string'),

    campaigns: hasMany()
});

models/client.js

export default DS.Model.extend({
    name: DS.attr('string'),
    startdate: DS.attr('date'),
    enddate: DS.attr('date'),

    client: DS.belongsTo('client')
});

/templates/client/edit.bhs

    <table class="table table-bordered table-striped table-condensed">
        <thead>
            <tr>
                <th></th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            {{#each model.campaigns as |campaign|}}
                <tr>
                    <td>{{campaign.name}}</td>
                </tr>
            {{/each}}
        </tbody>
    </table>

http://localhost:3000/api/clients/1

{
{
  "links": {
    "self": "http://localhost:3000/api/clients/1"
  },
  "data": {
    "type": "clients",
    "relationships": {
      "campaigns": {
        "links": {
          "related": "http://localhost:3000/api/clients/1/campaigns"
        }
      }
    },
    "id": "1",
    "attributes": {
      "name": "Test",
      "telno": "123"
    },
    "links": {
      "self": "http://localhost:3000/api/clients/1"
    }
  }
}

http://localhost:3000/api/clients/1/campaigns

{
  "links": {
    "self": "http://localhost:3000/api/clients/1/campaigns"
  },
  "data": [
    {
      "type": "campaigns",
      "relationships": {
        "client": {
          "links": {
            "related": "http://localhost:3000/api/campaigns/1/client"
          }
        }
      },
      "id": "1",
      "attributes": {
        "enddate": "2019-01-01T00:00:00.000Z",
        "name": "test",
        "startdate": null
      },
      "links": {
        "self": "http://localhost:3000/api/campaigns/1"
      }
    }
  ]
}

1 Comment

We decided to go with the ember-data-url-templates approach because we need to call the nested route for POST and PATCH, too (see my comment above). Thank you for posting this anyway, i'll try this approach next time when i only need GET!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.