Skip to content

Instantly share code, notes, and snippets.

@jamesarosen
Last active May 24, 2018 08:44
Show Gist options
  • Save jamesarosen/a7971082162b876db9b8 to your computer and use it in GitHub Desktop.
Save jamesarosen/a7971082162b876db9b8 to your computer and use it in GitHub Desktop.
Ember-CLI + Ember-Data + Custom API

I had some trouble getting Ember-CLI and Ember-Data working with my existing HTTP API. These are the things I had to do to get it working:

Use ActiveModelSerializer by default

My API returns underscored_keys, not camelizedKeys. It's not exactly the standard ActiveModel, but it's quite similar. I started by defining a default (application) serializer that's a simple subclass of DS.ActiveModelSerializer:

// app/serializers/application.js
import DS from "ember-data";

export default DS.ActiveModelSerializer.extend({
});

Custom URL Structure

One of my models -- Billing -- has a rather funky URL: /billing/no_list=true&customer_id=:id. To accomodate that, I defined a BillingAdapter:

// app/billing/adapter.js (I use pods; otherwise, it would go in `app/adapters/billing.js`)
import DS from "ember-data";

export default DS.RESTAdapter.extend({
  buildURL: function(type, id, record) {
    return '/billing?no_list=true&customer_id=%@'.fmt(id);
  }
});

No JSON Root

Now Ember-Data is fetching my model, but it blows up when trying to populate it. Cryptically, Ember-Data tells me

Error while processing route: billing.overview Assertion Failed: Expected an object as `data` in a call to `push` for myapp@model:billing: , but was undefined

To fix this, I defined a BillingSerializer that inserts a root billing object in the payload:

// app/billing/serializer:
import AppSerializer from "tango/serializers/application";

export default AppSerializer.extend({
  extractSingle: function(store, type, payload, id, requestType) {
    return this._super(store, type, { billing: payload }, id, requestType);
  }
});

ID-less API

Closer still! Now Ember-Data complains that the API response doesn't include an id, which Ember-Data will need if I ever update or destroy the record. It gives me a better error message for this problem:

Error while processing route: billing.overview Assertion Failed: You must include an `id` for myapp@model:billing: in an object passed to `push`

To compensate, I added the id in BillingSerializer#extractSingle:

// app/billing/serializer:
import AppSerializer from "tango/serializers/application";

export default AppSerializer.extend({
  extractSingle: function(store, type, payload, id, requestType) {
    payload.id = id;
    return this._super(store, type, { billing: payload }, id, requestType);
  }
});

Nested Objects

The /billing API returns a deeply-nested JSON structure. I could translate those into other models and declare has-one relationships, but really it's just structured data. No other objects in the system have references to these objects. Ember-Data doesn't have a way for me to declare these attributes, so I created an ObjectTransform:

// app/transforms/object.js
import DS from "ember-data";

export default DS.Transform.extend({
  deserialize: function(serialized) {
    return serialized;
  },

  serialize: function(deserialized) {
    return deserialized;
  }
});

Then I used it in the model:

// app/billing/model.js
import DS from "ember-data";

export default DS.Model.extend({
  lastBillied: DS.attr('date'),
  invoice: DS.attr('object')
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment