You're viewing the legacy docs. They are deprecated as of May 18, 2016.
These docs are for version 2.4.2 and below of the Javascript SDK. Go to our current docs, or see our Web migration guide.

Guide

EmberFire

EmberJS is a framework for building ambitious web applications that utilizes auto-updating Handlebars templates, components, and routing with great URL support. You can easily add a realtime backend to your Ember app with EmberFire, our officialy supported adapter for using Firebase with Ember Data.

What is EmberFire?

EmberFire is the officially supported adapter for using Firebase with Ember Data, a library developed and maintained by the Ember Core Team for managing model data in your Ember apps. It is designed to be agnostic to the underlying backend used in your app.

Using our Firebase adapter, all of your models stored in Ember Data will automatically be synchronized with our remote Firebase database.

This guide assumes you are familiar with the basics of Ember and Ember Data.

Integrating Firebase

Using Firebase, developers can add a realtime backend to their Ember app without setting up a server. Firebase's Ember library EmberFire integrates directly with Ember Data, so all of the data for your app is automatically persisted.

Tom Dale, one of the creators of Ember, explains:

With first-class support for Ember.js, Firebase developers can continue pushing the boundaries of what's possible in the browser by leaning on the strong architectural features of Ember that lead your app towards clean separation of concerns.

Tom Dale, Co-creator of EmberJS
This guide uses ember-cli

This guide assumes you are using EmberFire as an addon with an ember-cli app. Not using ember-cli? Follow the instructions here to get started.

Firebase's realtime data synchronization is a great fit with Ember's frontend philosophy. Using EmberFire, we can interact with Ember Data as we normally would, and our app's data will automatically be persisted. Adding EmberFire to your ember-cli app is simple.

1. Install emberfire as an ember-cli addon

In order to use EmberFire in our project, we can run the following in our ember-cli app's directory:

$ ember install emberfire

This will add Firebase to your bower.json file and generate app/adapters/application.js for you with the following content:

// app/adapters/application.js
import config from '../config/environment';
import Firebase from 'firebase';
import FirebaseAdapter from 'emberfire/adapters/firebase';

export default FirebaseAdapter.extend({
  firebase: new Firebase(config.firebase)
});

2. Configure your Firebase Database URL

We'll build a blogging app to demonstrate how to store and sync data with EmberFire. Full code for this app is available on GitHub.

The FirebaseAdapter takes a Firebase database URL, which is the location where the blogging app data will be stored. Next, enter your Firebase database URL in config/environment.js:

firebase: 'https://YOUR-FIREBASE-APP.firebaseio.com/'

Your database data will now be synced directly with the Ember Data store, and you can interact with the data store as you normally would.

Store and Sync Data in Realtime

First, let's create a model for our posts. Each post will have a title, body and timestamp. We'll use the timestamp property to display the most recent post first:

$ ember generate model post title:string body:string timestamp:number
// app/models/post.js
export default DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  timestamp: DS.attr('number')
});

To add blog posts to our app, we'll create a template with a form for submitting blog posts:

$ ember generate template posts
<!-- app/templates/posts.hbs -->

<h2>New Post</h2>
<ul class="post-publish">
  <li>
    {{input value=title placeholder="Title"}}
  </li>
  <li>
    {{textarea value=body placeholder="Body"}}
  </li>
  <li>
    <button {{action "publishPost"}}>Publish</button>
  </li>
</ul>

We haven't written the publishPost action yet, so let's do that now in our PostsController:

$ ember generate controller posts
// app/controllers/posts.js
export default Ember.ArrayController.extend({
  sortProperties: ['timestamp'],
  sortAscending: false, // sorts post by timestamp
  actions: {
    publishPost: function() {
      var newPost = this.store.createRecord('post', {
        title: this.get('title'),
        body: this.get('body'),
        timestamp: new Date().getTime()
      });
      newPost.save();
    }
  }
});

We used an ArrayController to sort our posts by timestamp. In our publishPost action, we create a new post in the data store with the title and body entered in our Handlebars template. Simply calling newPost.save() will save our post to the data store and automatically create a record in the database.

EmberFire uses Firebase's push() function under the hood, which creates a unique timestamp-based ID for each record that is added to the database. Our data now looks like this:

{
  "posts": {
    "-JS4hh97qukW9_JWoPRu": {
      "body": "You can store and sync data in realtime without a backend.",
      "title": "EmberFire is flaming hot!",
      "timestamp": 1425940107418
    }
  }
}

Notice that our data is stored under a posts path. EmberFire will automatically try to determine the correct Firebase reference based on the model name. Since we have a Post model, all of our posts our automatically stored under a posts path in the database.

To retrieve the post data from the database, we just need to add a model hook to our posts route:

$ ember generate route posts
// app/routes/posts.js
export default Ember.Route.extend({
  model: function() {
    return this.store.find('post');
  }
});

Now we have access to all of our posts, and can display them in our template:

$ ember generate template posts
<!-- app/templates/posts.hbs -->

<section>
{{#each post in model}}
  <div>{{post.title}}</div>
  <div>{{post.body}}</div>
{{/each}}
</section>

Querying Data

In our posts template, let's say we want to display the ten most recent blog posts. We can do this with by adding the following parameters to our posts route:

export default Ember.Route.extend({
  model: function() {
    return this.store.find('post', {
      orderBy: 'published',
      limitToLast: 10
    });
  }
});

We can combine orderBy with limitToFirst, startAt, endAt, and equalTo to construct more complex queries. See the EmberFire API docs and documentation on Firebase's querying methods for more details.

Data Structure

By default, EmberFire will try to determine the correct Firebase reference based on the model name. If we have a Post model, the records will be stored in the database under a /posts URL, and when we call find() on a model records will be fetched from https://your-firebase.firebaseio.com/posts. New records will be saved to https://your-firebase.firebaseio.com/posts/post_id.

Naming data differently

If you would like to name your data differently and customize where a model will be fetched and saved, you can create a model specific adapter that extends from your application adapter like the following:

// app/adapters/post.js
import ApplicationAdapter from './application';

export default ApplicationAdapter.extend({
  pathForType: function(type) {
    return "custom-posts";
  }
});

Overriding the pathForType() method will allow you to tell the adapter where it should fetch and save records of the specified type. Records will now be fetched from https://your-firebase.firebaseio.com/custom-posts and saved to https://your-firebase.firebaseio.com/custom-posts/post_id.

Relationships

EmberFire can handle relationships in two different ways: async and embedded, and currently supports hasMany and belongsTo relationships.

Async

Any relationship that is flagged as async: true tells the adapter to fetch the record if it hasn't already been loaded.

// app/models/post.js
import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment', { async: true })
});

In this example, comments will be fetched from https://your-firebase.firebaseio.com/comments. Here is what the data structure would look like in the database:

{
  "posts": {
    "post_id_1": {
      "comments": {
        "comment_id_1": true
      }
    }
  },
  "comments": {
    "comment_id_1": {
      "body": "This is a comment",
      "post": "post_id_1"
    }
  }
}
Defining both sides of a relationship

You only need to define an async hasMany - belongsTo relationship on both sides if you need to access the data in both ways (e.g. somePost.comments and someComment.post). When you do define both sides of a relationship, be sure to save both sides when making changes.

Saving Async Relationship Data

When saving async hasMany - belongsTo relationships in EmberFire, both sides of the relationship should be saved. To do this in our blog example, we'll push the comment to the store, save the comment, and finally saved the parent post:

// app/controllers/comments/new.js

// Create the comment
var newComment = this.store.createRecord('comment', {
  body: 'My awesome new comment'
});

// Get the parent post
var post = this.get('post');
post.get('comments').addObject(newComment);

// Save the comment, then save the post
newComment.save().then(function() {
  return post.save();
});

Removing Async Relationship Data

When removing a record that has children, you should delete the record's children before deleting the parent. In our example, we can do the following to delete a post:

// app/controllers/posts.js

var post = this.get('post');
var deletions = post.get('comments').map(function(comment) {
  return comment.destroyRecord();
});

// Ensures all comments are deleted before the post
Ember.RSVP.all(deletions)
  .then(function() {
  return post.destroyRecord();
})
.catch(function(e) {
  // Handle errors
});

When removing a child record, Ember Data automatically updates the relationship for you. You just need to save the parent record. To remove a comment in our example we can do the following:

// app/controllers/comments.js

var comment = this.get('comment');
var post = comment.get('post');

comment.destroyRecord().then(function() {
  post.save();
});

Embedded

Any relationship that is flagged as async: false tells the adapter that the related records have been included in the payload. Generally, this approach is more complicated and not as widely used, but it has been included to support existing data structures.

Async: True is now the default in EmberFire 1.5.x

In previous versions of EmberFire, embedded records were defined in the model with embedded: true. In EmberFire versions 1.5.0 and above, using embedded: true will not work. Be sure to use async: false, remove any mention of embedded in the model definition, and create a serializer as described below.

To define embedded records, we can do the following in our Post model:

// app/models/post.js
import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment', { async: false }),
  metaData: DS.belongsTo('post', { async: false })
});

When setting up embedded records we also need to create a serializer with an attrs object, like the following:

// app/serializers/post.js
import FirebaseSerializer from 'emberfire/serializers/firebase';

export default FirebaseSerializer.extend({
  attrs: {
    comments: { embedded: 'always' },
    metaData: { embedded: 'always' }
  }
});

Now that we've created a serializer to embed the records, here's what the data structure of our embedded comment records would look like in the database:

{
  "posts": {
    "post_id_1": {
      "comments": {
        "comment_id_1": {
          "body": "This is a comment"
        }
      },
      "metaData": {
        "id": "metaData_1",
        "createdAt": 1438039744053,
        "editedAt": 1438039761880
      }
    }
  }
}

When a model has embedded relationships, the related model should not be saved on its own. Instead, the comment needs to be added to the post and then the post can be saved.

Saving Embedded Relationship Data

There is no need to save embedded records directly, since this is handled automatically by EmberFire. When you save the parent, the embedded record changes are also saved. To save a comment with an embedded relationship in our example, we would do the following:

// app/controllers/comments/new.js

var post = this.get('post');
var newComment = this.store.createRecord({
  body: 'My super fun embedded comment'
});

post.get('comments').then(function(comments) {
  comments.addObject(newComment);
  // The comment is automatically saved when we call save() on the parent:
  return post.save();
});

Removing Embedded Relationship Data

When removing a parent record with an embedded relationship, we don't need to go through and delete the children first since they will be deleted automatically.

// app/controllers/posts.js

var post = this.get('post');

// Automatically deletes all children
post.destroyRecord();

Authentication

We can add authentication to our app using a torii provider built into EmberFire. The first step to set up authentication in our app is installing the torii addon by running:

ember install torii

Torii can provide your app with a session service that holds the currently logged in user. We'll configure that in config/environment.js:

// config/environment.js
/* ... */
    firebase: 'https://YOUR-FIREBASE-NAME.firebaseio.com/',
    torii: {
      sessionServiceName: 'session'
    }
/* ... */

This will inject a session property into our routes and controllers.

In order to use Torii, we need to create a app/torii-adapters/application.js adapter file with the following code:

import Ember from 'ember';
import ToriiFirebaseAdapter from 'emberfire/torii-adapters/firebase';

export default ToriiFirebaseAdapter.extend({
  firebase: Ember.inject.service()
});

The next step is to enable an authentication provider in your Firebase Dashboard, and enter the API key and secret for that provider. Details on enabling third-party providers can be found in our web guide here.

In this example we'll use Twitter authentication. To start, we'll define signIn and signOut actions in our application route making use of the session variable:

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

export default Ember.Route.extend({
  beforeModel: function() {
    return this.get("session").fetch().catch(function() {});
  },

  actions: {
    signIn: function(provider) {
      this.get("session").open("firebase", { provider: provider}).then(function(data) {
        console.log(data.currentUser);
      });
    },

    signOut: function() {
      this.get("session").close();
    }
  }
});

In our beforeModel hook we call fetch, which fetches the current user's session if it exists. Then in the signIn action. we pass Firebase the name of the provider we're using. This returns a promise with data on the authenticated user. In the example above we're logging the user object to the console.

In app/templates/application.hbs we'll call our signIn action when a user clicks the "Sign in with Twitter" button, passing "twitter" as the provider parameter. This makes use of our session variable to display the sign in button only to unauthenticated users.

// app/templates/application.hbs
{{#if session.isAuthenticated}}
  Logged in as {{session.currentUser.displayName}}
  <button {{action "signOut"}}>Sign out</button>
  {{outlet}}
{{else}}
  <button {{action "signIn" "twitter"}}>Sign in with Twitter</button>
{{/if}}

Authentication with Facebook, GitHub, and Google work similarly once you've enabled them in your Firebase Dashboard.

To use Firebase's email & password authentication, use password as the provider and pass it the user's email and password:

this.get('session').open('firebase', {
  provider: 'password',
  email: 'test@example.com',
  password: 'password1234'
});

Using EmberFire without Ember CLI

EmberFire also works without ember-cli. We can add EmberFire to an app that doesn't use ember-cli in two simple steps:

1. Include Dependencies

To use EmberFire in our project, we'll need to include the following dependencies:

<!-- Ember + Ember Data -->
<script src="http://builds.emberjs.com/tags/v1.9.1/ember.min.js"></script>
<script src="http://builds.emberjs.com/tags/v1.0.0-beta.12/ember-data.min.js"></script>

<!-- Firebase -->
<script src="https://cdn.firebase.com/js/client/2.4.2/firebase.js"></script>

<!-- EmberFire -->
<script src="https://cdn.firebase.com/libs/emberfire/1.6.6/emberfire.min.js"></script>

2. Initialize the FirebaseAdapter

Now that we've included EmberFire and its dependencies, we can create an instance of DS.FirebaseAdapter in our app:

App.ApplicationAdapter = DS.FirebaseAdapter.extend({
  firebase: new Firebase('https://docs-examples.firebaseio.com/web/bindings/ember/blog')
});

Our remote database data will now be synced directly with the Ember Data store.

Deploy Your Ember App

We're ready to deploy our Ember app! Using Firebase Hosting, we can deploy our application's static files (HTML, CSS, JavaScript, etc) to the web with a single command. To get started, we'll download firebase-tools via npm:

$ npm install -g firebase-tools

Read through our hosting quickstart to get your site up and running in minutes. Firebase Hosting is a production-grade service, with security, reliability, and scalability baked-in. We host your content on a global CDN and even provision an SSL certificate automatically for you.

For ember-cli apps, run the following commands:

$ ember build
$ firebase init

Then choose the name of the Firebase app you're deploying and enter dist when prompted for your public directory. This will generate a firebase.json file. Update the file to include the following rewrites configuration:

{
  "firebase": "my-ember-cli-app",
  "public": "dist",
  "rewrites": [{
    "source": "**",
    "destination": "/index.html"
  }]
}

Deploy your app by running the command:

$ firebase deploy
Paid Firebase apps can serve their files from a custom domain name!

Note that you can use any hosting service you'd like to deploy your Ember app, you don't need to use Firebase Hosting.

Next Steps

EmberJS is a great frontend framework for creating ambitious applications. With Firebase as the backend for your Ember app, you can create complex, beautiful apps without worrying about setting up a server or writing any backend code.

Now that you're up and running with Ember and Firebase, you can join our community to ask technical questions and share your apps. Use the emberfire tag on Stack Overflow, or join our Firebase + Ember Google Group.