You define route handlers using the verb methods (get
, post
, put
/patch
, and del
). All handlers have the following signature:
this.verb(path, handler[, responseCode, options={}]);
There are three types of route handlers: shorthand, object, and function.
The status code for all three types defaults to the following, based on the verb being used for the route:
PUT/PATCH and POST change to 200 if there is a response body.
The optional options
hash passed as the last parameter has timing
and coalesce
options.
# options.timing
Set the timing parameter of the response for this particular route. Default is a 400ms delay during development and 0 delay in testing (so your tests run fast).
Note you can set a global timing parameter for all routes. Individual timing parameters override the global setting.
Example:
this.post('/users', { timing: 1500 });
this.get('/complex_query', () => {
return [1, 2, 3, 4, 5];
}, { timing: 3000 });
# options.coalesce
This option only affects the Array of Objects version of the GET shorthand. It is ignored by all other route handlers.
this.verb(path, shorthand[, responseCode]);
shorthand can either be a string, an array, or undefined
, depending on which shorthand you’re using. View the reference for all available shorthands.
Examples:
this.get('/api/authors');
this.put('/api/authors/:id');
this.del('/posts/:id');
this.verb(path, object[, responseCode]);
object is a POJO that’s returned for this route.
Example:
this.get('/api/authors/current', {id: 1, name: 'Link'});
this.get('/some/secret', {message: 'unauthorized'}, 404);
Write a custom function to handle this route.
this.verb(path, (schema, request) => {
// your code
}[, responseCode]);
The function handler you define takes two parameters, schema (your Mirage server’s ORM) and request (the Pretender request object). Consult the schema’s API for how to interact with your models (or the database directly) and Pretender’s docs for more info on the request object.
If the data returned from your handler is a JavaScript object or array, it will be stringified and sent as the response body of your request:
this.get('/api/events', () => {
let events = [];
for (var i = 0; i < 100; i++) {
events.push({
id: i,
value: Math.random() * 100
});
};
return events; // will be JSON.stringified by Mirage
});
Returning a Model or Collection (from schema
) lets you take advantage of the serializer layer.
this.get('/api/users/:id', ({ users }, request) => {
return users.find(request.params.id);
});
This handler returns a User model, which will pass through the UserSerializer if one exists, or the ApplicationSerializer otherwise.
You can also return an instance of Response
to dynamically set headers and the status code:
import Response from 'ember-cli-mirage/response';
this.post('/api/messages', (schema, request) => {
var params = JSON.parse(request.requestBody);
if (!params.title) {
return new Response(422, {some: 'header', 'Content-Type': 'application/json'}, {
errors: [{
status: 422,
title: 'email is invalid',
description: 'email cannot be blank'
}]
});
} else {
return schema.messages.create(params);
}
});
If you return a string, it will not be JSON.stringified
, so you can return responses other than JSON.
You may optionally return a promise resolving to any of the above, e.g.:
this.get('/users', () => {
return new Promise(resolve => {
resolve(new Response(200, { 'Content-Type': 'text/csv' }, 'firstname,lastname\nbob,dylan'));
});
});
The following helpers are available in your function handlers.
# this.serialize(modelOrCollection[, serializerName])
This helper returns the serialized JSON for the given modelOrCollection. It’s useful if you want to do some final munging on the serialized JSON before returning it.
this.get('/api/authors', function({ authors }) {
let json = this.serialize(authors.all());
json.meta = { page: 1 };
return json;
});
By default this method uses the named serializer for the given Model or Collection. You can pass in a specific serializer via serializerName
:
this.get('/api/authors', function({ authors }) {
let json = this.serialize(authors.all(), 'sparse-author');
json.meta = { page: 1 };
return json;
});
# this.normalizedRequestAttrs([ modelName ]))
This helper will return the body of a request in a normalized form, suitable for working with and creating records.
For example, if your Ember app makes a POST request with this data
POST /users
{
data: {
type: 'users',
attributes: {
'first-name': 'Conan',
'middle-name': 'the',
'last-name': 'Barbarian'
},
relationships: {
team: {
data: {
type: 'teams',
id: 1
}
}
}
}
}
then normalizedRequestAttrs()
could be used like this
this.post('/users', function(schema, request) {
let attrs = this.normalizedRequestAttrs();
/*
attrs = {
firstName: 'Conan',
middleName: 'the',
lastName: 'Barbarian',
teamId: '1'
}
*/
return schema.users.create(attrs);
});
Note that attribute keys were camelCased, and the team
foreign key was extracted. This is because a user
owns the team
foreign key; if another relationship were included in the request but the user
did not own its foreign key, it would not have been extracted.
Note that this helper method leverages your serializer’s normalize
function. In the example above, it’s assumed that the app was using the JSONAPISerializer
, which comes with the #normalize
method already written. If you’re not using one of the bundled serializers, you’ll need to implement #normalize
and have it return a JSON:API document to take advantage of this method.
Additionally, you’ll need to use a full function
here, as opposed to an ES6 arrow function (e.g () => { ... }
). This is because normalizeRequestAttrs
requires the this
context from the function handler, and an arrow function would bind this from the outer scope.
normalizedRequestAttrs()
relies on a modelName
to work and attempts to automatically detect it based on the URL of the request. If you use conventional URLs – for example, PATCH /users/1
– the helper should work. If you are using something custom – for example, PATCH /users/edit/1
– you’ll need to provide the modelName
to the helper:
this.patch('/users/edit/:id', function(schema, request) {
let attrs = this.normalizedRequestAttrs('user');
...
});
You can use Mirage to simulate other-origin requests. By default, a route like
this.get('/contacts', ...)
will hit the same origin that’s serving your Ember app. To handle a different origin, use the fully qualified domain name:
this.get('http://api.twitter.com/v1', ...)
If your entire Ember app uses an external (other-origin) API, you can globally configure the domain via urlPrefix
:
// mirage/config.js
this.urlPrefix = 'https://my.api.com';
// This route will handle requests to https://my.api.com/contacts
this.get('/contacts', ...)