Search code examples
javascriptember.jsember-data

How to use adapters, serializers, models and other tools in Ember more effectively?


I have an app based on Ember Data. I would like to figure out how to make the code more compact. My application looks like this:: there are several endpoints that from the Express server. In the Ember application, each endpoint corresponds to: adapter, serializer, model, route, template. Everything works fine, but the code is too cumbersome. I'm new to Ember, and maybe there is a way to use adapters and other tools more universally. Here are some parts of my application that illustrate how it works.

localhost:5000/api/cars

localhost:5000/api/vehicles

Adapter "cars":

import RESTAdapter from '@ember-data/adapter/rest';
export default RESTAdapter.extend({
  host: http://localhost:5000/api,
  pathForType() {
    return "cars";
  }
});

Adapter "vehicles":

import RESTAdapter from '@ember-data/adapter/rest';
export default RESTAdapter.extend({
  host: http://localhost:5000/api,
  pathForType() {
    return "vehicles";
  }
});

Serializer "cars":

import RESTSerializer from '@ember-data/serializer/rest';
export default RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = {
      cars: payload
    };
    return this._super(store, primaryModelClass, payload, id, requestType);
  },
  primaryKey: '_id'
});

Serializer "vehicles":

import RESTSerializer from '@ember-data/serializer/rest';
export default RESTSerializer.extend({
  normalizeResponse(store, primaryModelClass, payload, id, requestType) {
    payload = {
      vehicles: payload
    };
    return this._super(store, primaryModelClass, payload, id, requestType);
  },
  primaryKey: '_id'
});

Car model:

import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
  name: attr("string"),
  body: attr("array"),
  date: attr('date')
});

Vehicle model (the same as car!):

import DS from 'ember-data';
const { attr } = DS;
export default DS.Model.extend({
  name: attr("string"),
  body: attr("array"),
  date: attr('date')
});

Cars index route:

import Route from '@ember/routing/route';
export default class CarsIndexRoute extends Route {
  model() {
    return this.store.findAll("car");
  }
}

Vehicles index route:

import Route from '@ember/routing/route';
export default class VehiclesIndexRoute extends Route {
  model() {
    return this.store.findAll("vehicle");
  }
}

Hbs templates are completely similar. cars/index.hbs (and vehicles/index.hbs):

{{#each @model as |item|}}
<h3>{{item.name}}</h3>
<p>{{item.body}}</p>
{{/each}}

The code clearly shows that the structure is the same, and the differences are only in one parameter, which corresponds to the model name and the "end" of the api endpoint. Can someone tell me how to organize everything more correctly, in the tradition of Ember? Thanks!


Solution

  • The great thing about Ember is that it's convention based. In your case that means once /cars and /vehicles sit at the same endpoint (which they do) and have the same structure (which they seem to do as well) you simply need a single adapter and serializer for all of them.

    import RESTAdapter from '@ember-data/adapter/rest';
    
    export default class ApplicationAdapter extends RESTAdapter {
      host = "http://localhost:5000";
      namespace = 'api'
    } 
    
    import RESTSerializer from '@ember-data/serializer/rest';
    
    export default class ApplicationSerializer extends RESTSerializer {
      primaryKey = '_id';
    
      // not sure exactly what happens in the payload but I am pretty sure you'd be able to generalize it correspondingly
    }
    

    as for models and routes I'd leave them as they are or extracted a superclass if needed.