How does ember expects information to be returned?


#1

Hi.

I have been working with ember for about a year to mock up applications in a really fast and lovely way, after getting frustrated about how angular works. Of course all of this is done using ember-cli-mirage.

I am now to the point where I want to use ember to connect to real databases to deploy the apps instead of just mocking them. Those apps have been developed in php as that’s what I have mastered, in order to meet the deadline. However I cannot figure out how to model my data (or structure my queries) in order to be retrieved properly by ember, specially when it comes to relationships. I am working with a handmade PHP API and MySQL database.

For example, let’s say I have the following models:

models/house.js

export default DS.Model.extend({
  street: DS.attr(),
  number: DS.attr(),
  neighbourhood: DS.belongsTo('neighbourhood'),
  rooms: DS.hasMany('room')
})

models/room.js

export default DS.Model.extend({
  name: DS.attr(),
  width: DS.attr(),
  height: DS.attr(),
  house: DS.belongsTo('house')
})

Let’s say I have a route called house/

routes/house.js

export default Route.extend({
  model () {
    return this.get('store').findAll('house')
  }
})

I expect this to return a list of houses with their rooms as a nested list on each house, as it does if done with mirage and factories. However this only returns the list of houses. The table “houses” does not have any reference to the rooms. Instead, the table rooms has a “houseId” on each room to link them.

If I populate my template with that model:

templates/house.hbs

{{#each model as |model|}}
    <p>{{model.street}}</p>
    <p>{{model.number}}</p>
    <p>{{model.rooms}}</p>
{{/each}}

This is the output:

5th Ave.
#454
<DS.PromiseManyArray:ember583>

How should I handle this? Anything that points me to the right direction will be truly appreciated. I want to convince my team to develop our apps purely in ember and eventually to change our servers to node, but I have to make one step at a time and it will be to switch from php frontends to ember.

Thank you very much in advance.

Mike


#2

ember-data does not expect anything but your Adapter does. If you use the JSONAPI Adapter you can use Simple-Json-PHP to create the json. We did something like this for a customer but because we needed offline first support we synced the MySQL database with a CouchDB database on the backend and used the ember-pouch Adapter to connect to the CouchDB backend.


#3

@spectrevil another option for home grown apis is to make fetch requests without the adapter/serializer. In my day to day we make web socket requests from the client and store the state in redux. Then we use reselect + ember components to show that state for the user and I’m happy to report some additional benefits like time travel debugging + hot module reloading as a result.

Here is a quick 12 min introduction using fetch with ember redux


#4

@spectrevil so at a really high level (and sorry if you are already familiar with the data layer but I want to cover all of the basics in case you’re not)…

Your ember application is using a specific data format internally. Ember prefers JSON-API formatted data by default. Because of Ember’s preference for JSON API format, Mirage emits JSON API data format by default. However Ember is designed to be backend agnostic so Ember Data provides a powerful set of abstractions so you can customize your serializer/adapter/model layer to fit any backend and the Ember app doesn’t care about how it all works other than consuming your models via the store methods.

Ember comes with 3 adapter/serializer sets by default:

  1. JSON adapter - most generic adapter/serializer, used for backends that produce pretty raw JSON format data e.g. GET /posts -> [{ id:1, ....}, {id:2, ...}]
  1. REST adapter/serializer - like json adapter/serializer format but response payloads are wrapped in outer objects, among a few other things (meta information like pagination, etc) e.g. GET /posts -> {posts: [{ id:1, ....}, {id:2, ...}] }
  2. JSON API adapter/serializer which implements the JSON API spec and is more verbose and formal but also more explicit and has support for more API features

Again, Ember prefers data in JSON API format by default. However while JSON API is becoming more heavily adopted a large number of people don’t have that API format yet (or may never) so I’d say probably the majority of individuals do some level of customization with adapters/serializers. The way I think about it (generally, this isn’t a strict rule) is that your adapter defines how requests are formed (e.g. the request URLs, the request types, the headers and query params, all that jazz) and the serializer is responsible for converting incoming/outgoing data between the JSON API format that Ember expects and whatever format your backend data is served/expected in.

Typically what you’d do is pick whichever of the three default adapter/serializer pairs matches your backend most closely and extend it with custom ones (you can defined an application serializer/adapter which apply to all models, or custom serializers/adapters for each model type, or a mix of the two). It really depends on how weird/custom your API format is in terms of how much you’ll have to customize the adapter/serializer layer. I’d definitely hit the docs and obviously feel free to ask more specific questions here if you get stuck. Alternatively you could post some of your payloads and URL formats and we can make some recommendations on how to proceed.

In terms of relationships… There are multiple ways you can define related data via your backend. You can include a link (e.g. /houses/1/rooms) and Ember will fetch the room data asynchronously, you can include ids (e.g. [1, 5, 8] and Ember will fetch the room data asynchronously by id using the room adapter (or the application adapter of course if that’s all you have defined). Lastly you can include the related room data in your house payload. Your backend determines how this data is included/linked and then your Ember adapter/serializer will determine how to fetch it/process it based on how it is served.

Anyway, if you think you use a pretty standard API design your customization job might be pretty easy and would be able to be shared across apps for the most part, but depending on the API design choices it might be more complicated. Like I said, feel free to share some API endpoint urls and response payloads so we can help more with specifics, or jump right in and post here if you get stuck.

Good luck!