Version:

Serializers are responsible for formatting your route handler’s response.

The application serializer (/mirage/serializers/application.js) will apply to every response. To make specific customizations, define per-model serializers (e.g. /mirage/serializers/blog-post.js).

Any Model or Collection returned from a route handler will pass through the serializer layer. Highest priority will be given to a model-specific serializer, then the application serializer, then the default serializer.

Mirage ships with three named serializers:

  • JSONAPISerializer, to simulate JSON:API compliant servers:

    // mirage/serializers/application.js
    import { JSONAPISerializer } from 'ember-cli-mirage';
    
    export default JSONAPISerializer;
    
  • ActiveModelSerializer, to fake Rails backends that use AMS-style responses:

    // mirage/serializers/application.js
    import { ActiveModelSerializer } from 'ember-cli-mirage';
    
    export default ActiveModelSerializer;
    
  • RestSerializer, to fake backends that match Ember Data’s RestSerializer expected response format:

    // mirage/serializers/application.js
    import { RestSerializer } from 'ember-cli-mirage';
    
    export default RestSerializer;
    

Additionally, Mirage has a basic Serializer class which you can customize using the hooks documented below:

// mirage/serializers/application.js
import { Serializer } from 'ember-cli-mirage';

export default Serializer;

When writing model-specific serializers, remember to extend from your application serializer:

// mirage/serializers/blog-post.js
import BaseSerializer from './application';

export default BaseSerializer.extend({
  include: ['comments']
});

serialize(response, request)

Override this method to implement your own custom serialize function. response is whatever was returned from your route handler, and request is the Pretender request object.

Returns a plain JavaScript object or array, which Mirage uses as the response data to your Ember app’s XHR request.

You can also override this method, call super, and manipulate the data before Mirage responds with it. This is a great place to add metadata, or for one-off operations that don’t fit neatly into any of Mirage’s other abstractions:

serialize(object, request) {
  // This is how to call super, as Mirage borrows [Backbone's implementation of extend](http://backbonejs.org/#Model-extend)
  let json = Serializer.prototype.serialize.apply(this, arguments);

  // Add metadata, sort parts of the response, etc.

  return json;
}

normalize(json)

This method is used by the POST and PUT shorthands. These shorthands expect a valid JSON:API document as part of the request, so that they know how to create or update the appropriate resouce. The normalize method allows you to transform your request body into a JSON:API document, which lets you take advantage of the shorthands when you otherwise may not be able to.

Note that this method is a noop if you’re using JSON:API already, since request payloads sent along with POST and PUT requests will already be in the correct format.

Take a look at the included ActiveModelSerializer’s normalize method for an example.


attrs

Use this property on a model serializer to whitelist attributes that will be used in your JSON payload.

For example, if you had a blog-post model in your database that looked like

{
  id: 1,
  title: 'Lorem ipsum',
  created_at: '2014-01-01 10:00:00',
  updated_at: '2014-01-03 11:42:12'
}

and you just wanted id and title, you could write

// mirage/serializers/blog-post.js

export default Serializer.extend({
  attrs: ['id', 'title']
});

and the payload would look like

{
  id: 1,
  title: 'Lorem ipsum'
}

include

Use this property on a model serializer to specify related models you’d like to include in your JSON payload.

For example, if you had an author with many blog-posts:

// mirage/models/author.js
export default Model.extend({
  blogPosts: hasMany()
});

and you wanted to sideload these, specify so in the include key:

// mirage/serializers/author.js
export default Serializer.extend({
  include: ['blogPosts']
});

Now a response to a request for an author would look like this:

GET /authors/1

{
  author: {
    id: 1,
    name: 'Link',
    blogPostIds: [1, 2]
  },
  blogPosts: [
    {id: 1, authorId: 1, title: 'Lorem'},
    {id: 2, authorId: 1, title: 'Ipsum'}
  ]
}

You can also define include as a function so it can be determined dynamically:

// mirage/serializers/author.js
export default Serializer.extend({
  include: function(request) {
    if (request.queryParams.posts) {
      return ['blogPosts'];
    } else {
      return [];
    }
  }
});

include query param

Note: This is only available when using the JSONAPISerializer.

The JSONAPISerializer supports the use of include query parameter to return compound documents. Prior to Ember Data 2.5, you will need to add 'ds-finder-include': true to your app FEATURES object:

// config/environment.js
var ENV = {
  EmberENV: {
    FEATURES: {
      'ds-finder-include': true
    }
  }
};

To tell Mirage to sideload blogPosts when we find find all authors we can do something like the following:

// routes/authors.js
export default Ember.Route.extend({
  model() {
    return this.store.findAll('author', {include: 'blogPosts'});
  }
}

The above will make a GET request to /api/authors?include=blogPosts, and then the appropriate Mirage route handler will be invoked. When it comes time to serialize the response, the JSONAPISerializer will inspect the query params of the request, see that the blogPosts relationship is present, and then proceed as if this relationship was specified directly in the include: [] array on the serializer itself.

Note that, in accordance with the spec, Mirage gives precedence to an ?include query param over a default include: [] array that you might have specified directly on the serializer. Default includes will still be in effect, however, if a request does not have an ?include query param.


root

default true

Set whether your JSON response should have a root key in it.

By default it does, so a request for an author looks like:

GET /authors/1

{
  author: {
    id: 1,
    name: 'Link'
  }
}

Setting root to false disables this:

// mirage/serializers/application.js
export default Serializer.extend({
  root: false
});

Now the response looks like:

GET /authors/1

{
  id: 1,
  name: 'Link'
}

embed

default false

Set whether related models should be embedded or sideloaded.

By default this false, so relationships are sideloaded:

GET /authors/1

{
  author: {
    id: 1,
    name: 'Link',
    blogPostIds: [1, 2]
  },
  blogPosts: [
    {id: 1, authorId: 1, title: 'Lorem'},
    {id: 2, authorId: 1, title: 'Ipsum'}
  ]
}

Setting embed to true will embed related records:

// mirage/serializers/application.js
export default Serializer.extend({
  embed: true
});

Now the response looks like:

GET /authors/1

{
  author: {
    id: 1,
    name: 'Link',
    blogPosts: [
      {id: 1, authorId: 1, title: 'Lorem'},
      {id: 2, authorId: 1, title: 'Ipsum'}
    ]
  ]
}

serializeIds

Note: this feature was added in 0.2.2.

Use this to define how your serializer handles serializing relationship keys. It can take one of three values:

  • included, which is the default, will serialize the ids of a relationship if that relationship is included (sideloaded) along with the model or collection in the response
  • always will always serialize the ids of all relationships for the model or collection in the response
  • never will never serialize the ids of relationships for the model or collection in the response

keyForModel(modelName)

Used to define a custom key when serializing a primary model of modelName modelName. For example, the default Serializer will return something like the following:

GET /blogPosts/1

{
  blogPost: {
    id: 1,
    title: 'Lorem ipsum'
  }
}

If your API uses hyphenated keys, you could overwrite keyForModel:

// serializers/application.js
export default Serializer.extend({
  keyForModel(modelName) {
    return Ember.String.dasherize(modelName);
  }
});

Now the response will look like

{
  'blog-post': {
    id: 1,
    title: 'Lorem ipsum'
  }
}

keyForCollection(modelName)

Used to customize the key when serializing a primary collection. By default this pluralizes the return value of keyForModel.

For example, by default the following request may look like:

GET /blogPosts

{
  blogPosts: [
    {
      id: 1,
      title: 'Lorem ipsum'
    },
    ...
  ]
}

If your API hyphenates keys, you could overwrite keyForCollection:

// serializers/application.js
const { dasherize, pluralize } = Ember.String;

export default Serializer.extend({
  keyForCollection(modelName) {
    return pluralize(dasherize(modelName));
  }
});

Now the response would look like:

{
  'blog-posts': [
    {
      id: 1,
      title: 'Lorem ipsum'
    },
    ...
  ]
}

keyForAttribute(attr)

Used to customize how a model’s attribute is formatted in your JSON payload.

By default, model attributes are camelCase:

GET /authors/1

{
  author: {
    firstName: 'Link',
    lastName: 'The WoodElf'
  }
}

If your API expects snake case, you could write the following:

// serializers/application.js
const { underscore } = Ember.String;

export default Serializer.extend({
  keyForAttribute(attr) {
    return underscore(attr);
  }
});

Now the response would look like:

{
  author: {
    first_name: 'Link',
    last_name: 'The WoodElf'
  }
}

keyForRelationship(relationship)

Use this hook to format the key for collections related to this model. relationship is the named parameter for the relationship.

For example, if you’re serializing an author that sideloads many blogPosts, the default response will look like:

{
  author: {...},
  blogPosts: [...]
}

Overwrite keyForRelationship to format this key:

// serializers/application.js
const { underscore } = Ember.String;

export default Serializer.extend({
  keyForRelationship(relationship) {
    return underscore(relationship);
  }
});

Now the response will look like this:

{
  author: {...},
  blog_posts: [...]
}

keyForRelationshipIds(relationship)

Use this hook to format the key for relationship ids in this model’s JSON representation.

For example, if you’re serializing an author that sideloads many blogPosts, your author JSON would include a blogPostIds key:

{
  author: {
    id: 1,
    blogPostIds: [1, 2, 3]
  },
  blogPosts: [...]
}

Overwrite keyForRelationshipIds to format this key:

// serializers/application.js
const { underscore } = Ember.String;

export default Serializer.extend({
  keyForRelationshipIds(relationship) {
    return underscore(relationship) + '_ids';
  }
});

Now the response will look like:

{
  author: {
    id: 1,
    blog_post_ids: [1, 2, 3]
  },
  blogPosts: [...]
}

typeKeyForModel(model)

This hook is only available on the JSONAPISerializer.

Use this hook to override the generated type for the JSON:API resource object. By default, type will be the plural and dasherized form of the model name.

links(model)

This hook is only available on the JSONAPISerializer.

Use this hook to add top-level links data to JSON:API resource objects. The argument is the model being serialized.

// serializers/author.js
export default JsonApiSerializer.extend({

  links(author) {
    return {
      'posts': {
        related: `/api/authors/${author.id}/posts`
      }
    };
  }

});