javascriptember.jsember-data

How to work with REST API using Ember data?


I'm trying to use ember data with REST API from server. The project is quiet simple and works fine with Fetch API, but i would like to implement the same result using Ember data and understand it's principles. I could not use ember twiddle with this configuration. Below will be the main parts of the project. It was made by Ember CLI version 4.0.1.

The JSON responce from url http://localhost:3000/api/v1/items looks like:

[
  {
    "id": 1,
    "name": "Item 1",
    "description": "12345",
    "created_at": "2022-01-07T11:43:21.755Z",
    "updated_at": "2022-01-07T11:43:21.755Z"
  },
  {
    "id": 2,
    "name": "Item 2",
    "description": "22222",
    "created_at": "2022-01-07T11:43:29.787Z",
    "updated_at": "2022-01-07T11:43:29.787Z"
  },
  {
    "id": 3,
    "name": "Item 3",
    "description": "33333",
    "created_at": "2022-01-07T11:43:37.885Z",
    "updated_at": "2022-01-07T11:43:37.885Z"
  }
]

The responce from a single item request such as http://localhost:3000/api/v1/items/1:

{
    "id": 1,
    "name": "Item 1",
    "description": "12345",
    "created_at": "2022-01-07T11:43:21.755Z",
    "updated_at": "2022-01-07T11:43:21.755Z"
}

app/models/item.js:

import Model, { attr } from '@ember-data/model';

export default class ItemModel extends Model {
  @attr name;
  @attr description;
}

app/routes/items.js:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class ItemsRoute extends Route {
  @service store;
  async model() {
    return this.store.findAll('item');
  }
}

app/routes/item.js:

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class ItemRoute extends Route {
  @service store;
  async model(params) {
    return this.store.findRecord('item', params.id);
  }
}

app/templates/items.hbs:

{{#each @model as |item|}}
  <LinkTo @route='item' @model={{item}}>{{item.name}}</LinkTo>
{{/each}}

app/adapters/application.js:

import RESTAdapter from '@ember-data/adapter/rest';

export default class ItemAdapter extends RESTAdapter {
  host = 'http://localhost:3000';
  namespace = 'api/v1';

  buildURL(...args) {
    return `${super.buildURL(...args)}.json`;
  }
}

app/serializers/application.js:

import RESTSerializer from '@ember-data/serializer/rest';

export default class ItemSerializer extends RESTSerializer {}

app/router.js:

import EmberRouter from '@ember/routing/router';
import config from 'dist/config/environment';

export default class Router extends EmberRouter {
  location = config.locationType;
  rootURL = config.rootURL;
}

Router.map(function () {
  this.route('items');
  this.route('item', { path: '/:id' });
});

When I move to route /items on the ember client, the following error appears in the console:

WARNING: Encountered "0" in payload, but no model was found for model name "0" (resolved model name using <dist@serializer:item::constructor>.modelNameFromPayloadKey("0"))

And the same errors for "1" and "2". The template doesn't render links in the each loop.

Probably, there are some features in Ember data with REST API, and I would really like to figure it out. Thanks for attention!


Solution

  • I believe you have this issue because your data is not in the RESTSerializer format but rather in the JSONSerializer one:

    the JSONSerializer expects the response to be a JSON object that looks similar to this

    {
      "id": "1",
      "title": "Rails is omakase",
      "tag": "rails",
      "comments": ["1", "2"]
    }
    

    So i guess you should just use this one.

    If for any reason you wanna keep the RESTSerializer you should return your data similar to this format:

    {
    "item":
      {
        "id": 1,
        "name": "Item 1",
        "description": "12345",
        "created_at": "2022-01-07T11:43:21.755Z",
        "updated_at": "2022-01-07T11:43:21.755Z"
      }
    }
    

    or for a list

    {
    "items": [
      {
        "id": 1,
        "name": "Item 1",
        "description": "12345",
        "created_at": "2022-01-07T11:43:21.755Z",
        "updated_at": "2022-01-07T11:43:21.755Z"
      },
      ...
    ]