Building an Ember App Without a Back-End

in Development

One advantage of rich browser applications is that server-side and client-side are cleanly separated from each other. That makes it easier for different teams to develop each part and enables these parts to be developed at a different pace. Going further, it is also possible to implement a feature solely in the browser application without relying on the back-end, by mocking out server responses. This could be extremely useful for prototyping, for example.

In this post, I’m going to show you how to develop a whole (though small) application in this fashion, without implementing the API server.

Meet Mirage

Historically, the way to mock out the server part in an Ember CLI project was to create “http mocks” for each resource endpoint. That involved writing a lot of boilerplate code, and you couldn’t reuse the code for another big use case where mocking was needed — in tests.

Recently, a fantastic Ember addon called Ember CLI Mirage has emerged to address this issue. In Mirage, the server mocks and models can easily be reused between the environments. It provides abstractions that are easy to work with and makes writing mocking code a joy. We’re going to use the latest stable version of the library, which should be installed as a standard Ember addon:

$ ember install ember-cli-mirage

The Application

We’re going to use the demo version of Rock and Roll with Ember.js, a reduced version of what is developed in my book. It mimics a music catalog, allowing you to create bands and songs for these bands and rating those songs.

The application looks like this:

Full app

And it has the following routes:

// app/router.js

(...)
Router.map(function() {
  this.route('bands', function() {
    this.route('band', { path: ':id' }, function() {
      this.route('songs');
    });
  });
});

In Ember, each route loads the data it needs so that the corresponding template can be rendered. The bands, bands.band, and bands.band.songs will load (respectively) all the bands, the band designated by the id in the URL, and the songs belonging to that band.

Fetching Data From the Back-end

When we load the application, we get a 404 error because it tries to fetch data from localhost:4200/api/bands and fails. Let’s first see why it queries that URL and then how to fix it by implementing our first route handler in Mirage.

In Ember Data, adapters are responsible to compose URLs for the back-end. When we open the app-wide application adapter, here is what we find:

// app/adapters/application.js
import ActiveModelAdapter from 'active-model-adapter';

export default ActiveModelAdapter.extend({
  namespace: 'api'
});

By virtue of the namespace definition, api will be prepended to all requests, and the name of the resource will be pluralized to fetch all of them. That gives us /api/bands when we tell the Ember Data store to load all bands. That is exactly what happens in the bands route:

// app/routes/bands.js
import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.store.findAll('band');
  }
});

Specifying Our First Mirage Route Handler

With that understanding, we can now turn to Mirage to pretend it’s the back-end and return all of the bands for that request. Defining routes (in other words, request paths that Mirage will handle) happens in the configuration file. Let’s open that file and make some changes so that Mirage handles the route to return all bands:

// mirage/config.js
export default function() {
  this.namespace = 'api';
  this.get('/bands', function(schema) {
    return schema.bands.all();
  });
}

The this.namespace mirrors the one found in the Ember Data adapter. This way, we will not have to prepend it to all our routes defined in the config file. Next, we define that the bands route should return the list of all bands. Mirage manages its ORM through the schema object, which is passed in as the first parameter to all route handler functions.

Let’s restart the ember server process that serves our Ember app and navigate to localhost:4200/bands in our browser. Here, we receive the following error:

Mirage: Your handler for the url /api/bands threw an error: Cannot read property 'all' of undefined

That makes sense, as we haven’t told Mirage anything about a model called Band, so let’s do it now:

$ ember generate mirage-model band

When our Ember app is live-reloaded by Ember CLI, the page is now rendered, and the error is replaced by a warning:

WARNING: Encountered "data" in payload, but no model was found for
model name "datum" (resolved model name using ...)

Mirage can now access its collection of bands and then uses its serializer to send it back through “the wire.” Since by default it uses the JSONApiSerializer (assuming a JSON API for the real back-end), it creates a top-level data key to contain all of the data. As we use an ActiveModelAdapter (and, therefore, an ActiveModelSerializer) in the app, we have to make Mirage comply with that. Let’s modify the serializer accordingly:

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

export default ActiveModelSerializer.extend({
});

Hooray! The warning is gone… but we still don’t see any bands:

No warning but no bands either

That shouldn’t come as a surprise, as we haven’t added any bands yet. It’s sad to see an empty list stare at us when there have been (and still are!) so many awesome rock bands, so let’s fix that quickly.

Seeding the Database

We’d like a given set of bands to exist in our application whenever it is loaded. What we need is fixtures and loading them to Mirage’s database at start time. So let’s create a fixture file for the Band model:

$ ember generate mirage-fixture bands

(Note: The model name has to be pluralized.)

That command has created a file under mirage/fixtures/bands.js, exporting an empty array:

// mirage/fixtures/bands.js
export default [
];

Let’s add a couple of great bands here:

export default [
  { id: 1, name: 'Pearl Jam' },
  { id: 2, name: 'Led Zeppelin' },
  { id: 3, name: 'Foo Fighters' },
  { id: 4, name: 'Radiohead' }
];

There is one more thing we need to do before we can see the list of these bands. Mirage creates a default scenario file when installed, and the existence of that file prevents our fixtures from being loaded. So let’s delete the whole scenarios folder:

$ rm -rf mirage/scenarios

With that, we finally have a list of bands, rendered from our database seed:

List of bands

Creating a New Band

We might perceive there is a text input to create new bands. For that to work, we’ll have to mock out the request that creates a new band on the back-end. By convention, that request is a POST, sent to /api/bands, so we’ll have to add a route handler for that request:

// mirage/config.js
export default function() {
  this.namespace = 'api';
  this.get('/bands', function(schema, request) {
    return schema.bands.all();
  });
  this.post('/bands', function(schema, request) {
    let attrs = JSON.parse(request.requestBody).band;
    return schema.bands.create(attrs);
  });
}

Whereas before we returned the list of bands already existing in the database, here we create a new one on the collection (schema.bands.create) based on what was sent in the payload. That works like a charm, and we can now add new bands:

Create new bands

Route Handler Shorthands

One great feature of Mirage is route handler shorthands. If certain route handlers behave according to the convention embodied in the adapter/serializer type, their implementation can be deduced. This holds in our case, so we can abbreviate our route handlers to the following, and everything will keep working:

// mirage/config.js
export default function() {
  this.namespace = 'api';
  this.get('/bands');
  this.post('/bands');
}

Conclusion

Hopefully you now have a good idea of how to create an Ember application without a back-end component. Things get more interesting and realistic when there are several model classes related to each other. If you’d like, you can check out the code in the GitHub repository.

Code School

Code School teaches web technologies in the comfort of your browser with video lessons, coding challenges, and screencasts. We strive to help you learn by doing.

Visit codeschool.com

About the Author

Balint Erdi

Balint Erdi

Ember.js consultant, author of Rock and Roll with Ember.js.

Might We Suggest

Try Ember Course

Pack your bags and help the Woodland Wanderers as you learn how to use Ember and Ember CLI to build your next ambitious web applications.

View Course
Improve your JavaScript skills

Spend some time with this powerful scripting language and learn to build lightweight applications with enhanced user interfaces.

View Path