Working with Ember Data, Node, Express and MongoDB

April 16th, 2015

ember-mongo

This article was written in April 2015. This would have been Ember Data v1. If you are using Ember Data v2 some of the API might have changed, this article does not reflect those changes!

I have really been enjoying working with EmberJS lately, once you get over the learning curve and understand how things should relate, it becomes really fast and fun!

Lets take a look at how we can use Ember Data with Node (or io.js),Express and MongoDB. For this example lets use the Ember CLI to start an Ember application fast!

If you don’t have the Ember CLI installed make sure you install it via npm.


npm install -g ember-cli

Setting up the Ember Project

With this installed, navigate to the location you use to store your projects. Once you are in there we can start a new application, note that you do not need to create a new folder for this application since Ember CLI will do that for you.


ember new emberMongo

This command will start the process of creating a new ’emberMongo’ folder and Ember application for you. I have noticed some issues where the npm,bower install will not run. If you notice that the node_modules/bower_components folder is not there, simply run npm install or bower install.

Setting up Node Project

We will also need to setup our Node server. Let’s do this in a separate folder, since these are separate elements, one is the front-end, one is the back-end.

If you do not have MongoDB installed follow the MongoDB installation guide on the their website.

Create a new folder, and name it whatever you would like. In this case I am going to name mine ’emberServer’, once inside that folder we need to install a few npm modules.

First run npm init to initialize a package.json file. Fill out the options as you require. Next we have to install the modules we will need.


npm install --save-dev express && npm install --save-dev mongoose

Here we install Express to handle our routing and Mongoose to handle interacting with our database.

Building the Sever

Inside your node project create a server.js file and add the following.


var express = require('express');
var mongoose = require('mongoose');

var app = express();

mongoose.connect('mongodb://localhost/emberData');

Here we are getting Express and Mongoose and setting up our server with var app = express();. We also tell Mongoose to connect to our MongoDB instance and look for a database called emberData, if no database exists it will create one for us! If you are unsure how to set up a MongoDB process check out Manage mongod Processes in the MongoDB documentation.

Before we get any further we will also want to make sure we set the appropriate headers for our server.


app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
  	res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  	res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
    next();
});

In this case we want to allow the server that Ember CLI will create to access our content, which is http://localhost:4200

Creating our Mongoose Model

On our server first we need to create the model that we will use for our data. For the example of this post we will have some sort of notes application. So we will create a Note model.

With Mongoose we first need to create our schema for our model. The schema is the structure that will define our model, is the title a string? or is a value a number. In Mongoose this will look something like this:


var noteSchema = new mongoose.Schema({
	title: 'string',
	content: 'string',
	author: 'string'
});

We use the schema to help define our model.


var NoteModel = mongoose.model('note',noteSchema);

Here we define our model, mongoose.model() takes two arguments a name and a schema. As an aside when we insert documents into the MongoDB based on this model the collection it creates the pluralized version of the model name. So inside our emberData database there will be a notes collection.

Creating a route

To set up a route in Express, we can use one of the many HTTP methods, in this case we will just look at GET. To create a GET method we use the .get() method from Express.

.get() takes two arguments, the string for the path and a callback function that is passed the original request and a response stream. We will use that response stream to send our data, but first lets send a quick message to say that it works. We also need to add the line app.listen('4500') after the route. This sets our server to listen on a specific port number, in this case 4500.


var express = require('express');
var mongoose = require('mongoose');

var app = express();

app.use(function(req, res, next) {
    res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
  	res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  	res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, OPTIONS');
    next();
});

mongoose.connect('mongodb://localhost/emberData');

var noteSchema = new mongoose.Schema({
	title: 'string',
	content: 'string',
	author: 'string'
});

var NoteModel = mongoose.model('note',noteSchema);
//New lines!
app.get('/api/',function(req,res) {
	res.send('Working');
});

app.listen('4500');


In order to test this in your terminal run node server, this will run our server, if there are no errors you know it is running. In a new terminal window lets use the curl command to test that our GET method is working.

curl http://localhost:4500/api

The response should be Working . If this is working lets go ahead an create the route for getting all our notes.


app.get('/api/notes', function(req,res) {
	NoteModel.find({},function(err,docs) {
		if(err) {
			res.send(err);
		}
		else {
			res.send(docs);
		}
	});
});

Before we test this route we need to add some data to it! I have created a gist of some data HERE you can use. To do this the follow commands are what you will need to enter, this is assuming you have a mongo instance running.


mongo //Will open the mongo shell

use emberData //Will select the database we want

db.notes.insert(... paste in content from gist ...);

To test that that data is in there we can run db.notes.find(). You should see the data we added as the output! If this is working we can now verify that our route is working, in the terminal we can now run curl http://localhost:4500/api/notes.

Now that we have a working endpoint lets set it up so that we can get it to play nicely with how Ember Data expects the our API responses to be formatted.


app.get('/api/notes', function(req,res) {
	NoteModel.find({},function(err,docs) {
		if(err) {
			res.send({error:err});
		}
		else {
			res.send({note:docs});
		}
	});
});

We change the error response to be res.send({error:err}); and the success to be res.send({note:docs}); where note is the name of the model we will create in Ember and docs is an array of our returned documents. At this point we are ready to jump into our Ember project and start getting our data!

Jumping into Ember

Go back to your Ember project and in the terminal in that directory run ember serve, this will start a server to run our ember application. Navigate to the url it provides you and you should see “Welcome to Ember.js”.

Much like we did in Mongoose, we need to define our Ember model. To do this using the Ember CLI we can run ember generate model note this will generate the files we need for a new note model, it will place it in app/models/note.js it will also create a test file for you! Open that file and and lets add our schema for how we want this model to be defined.


import DS from 'ember-data';

export default DS.Model.extend({
	title: DS.attr('string'),
	content: DS.attr('string'),
	author: DS.attr('string')
});

In Ember Data we use the .attr() method to define the type that our model will accept. Next we need to create a REST.adapter to load and save our data. Using the Ember CLI lets generate a new adapter ember generate adapter application. In the adapter file add the following.


import DS from 'ember-data';

export default DS.RESTAdapter.extend({
	namespace: 'api',
	host: 'http://localhost:4500'
});

Here we provide information about our API in terms of the namespace and the host url. In order for this to work properly we need too set a property in our Ember config file. In config/environment.js there is a variables called ENV it will be an object. We need to add a new property where we can define what sources this site is allowed to interact with.


...
contentSecurityPolicy: {
     'connect-src' : "'self' http://localhost:4500"
}
...

There is one last thing we need to do before we can actually get our data into our application, we need to create a serializers to convert the standard MongoDB _id to id. Using the Ember CLI let’s generate a new serializer, ember generate serializer application. Inside the serializer we need to set what the primary key is and create a method to covert it.


import DS from 'ember-data';

export default DS.RESTSerializer.extend({
	primaryKey: '_id',
    serializeId: function(id) {
        return id.toString();
    }
});

We are finally ready to display our data with Ember! To do so we need to create an application route, ember generate route application. When we generate a new application route it will ask you if you want to replace the original application template, just say no. In here it is as simple as saying:


import Ember from 'ember';

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

this.store.findAll('note') will make a GET request to the our serve at a http://localhost:4500/api/notes. We can finally access our data in an Ember template! Inside templates/application.hbs add the following lines.


{{#each model as |item|}} 
	

{{item.content}}

{{/each}}

And that is it! If you go and check your site in the browser you will see your content showing up! I hope this helps get you started with using MongoDB with Express to build an Ember application! For more information check out the guide on the Ember site.

In the future look for an article on working model relationships!

  • Pingback: Using Ember CLI working with Fixture data - Ryan Christiani - Front-End Developer - Ryan Christiani – Front-End Developer()

  • Pingback: Using Ember CLI and working with Ember Data fixtures - Ryan Christiani – Front-End Developer()

  • Micah Miller-Eshleman

    Hey, thanks for an awesome tutorial! I’m new to Mongo & Express and really appreciate the level of detail.

    • Ryan Christiani

      Late replay, but no problem. Glad it helped you!

  • Anthony Corrado

    I was really not looking forward to hooking up Ember to Node anytime soon. I just started learning Ember two weeks ago and was smack in the middle of the notorious learning curve. For whatever reason, decided to rewrite one of my Angular/Firebase apps using Ember/Node/Mongo just for a crash course. Getting Ember to play nice with the backend was what I feared most, but this tutorial got me through it with no hiccups!

    Thanks a million, Ryan!

    • Ryan Christiani

      You are welcome! Glad it helped!

  • Yassine Sania

    Really helpful, ty for your time!

    • Ryan Christiani

      No problem! Glad it helped.

      • Daniel

        Agree, great article – though it looks like you missed another .find() reference!

        “this.store.find(‘note’)”

  • Seena Rowhani

    A better way to do this would be to set your /public folder in your express app to be the /dist.

    Then you can run ember build –environment=production.

    No need for cross origin in your headers

  • Justin Coyne

    This was very helpful, but a few things are now deprecated. It would be great if you could find the time to update {{#each item in model}} to {{#each model as |item|}} and this.store.find(‘note’) to this.store.findAll(‘note’)

    • Ryan Christiani

      Updated to fix deprecations! Thanks!

      • Justin Coyne

        {{high-five}}

      • Daniel

        Looks like you missed another reference to ‘this.find()’!!!

  • Pingback: How to connect emberjs with mongodb - Quora()

  • Say What

    Everything seem to work for me upto the last curl command.
    However no data in browser, I am seeing the following error:

    Error while processing route: index Assertion Failed: normalizeResponse must return a valid JSON API document:
    * One or more of the following keys must be present: “data”, “errors”, “meta”. Error: Assertion Failed: normalizeResponse must return a valid JSON API document:
    * One or more of the following keys must be present: “data”, “errors”, “meta”.

    • Ryan Christiani

      Sorry for the late reply, but I bet that Ember Data has changed a bit. Maybe instead of returning {notes:docs}, it needs to be {data:docs} to match up with the JSON API Spec.

      Also note that this article was written almost a year ago, Ember Data has made some changes.

  • xed

    Thanks for the tutorial, you got me out of the dark 😀

    • Ryan Christiani

      Glad it helped!

  • Thapa Rayan

    not working

    • Ryan Christiani

      Can you elaborate more then just ‘not working’? That is very vague. Not this was written almost a year ago, so Ember Data has changed quite a bit!

  • https://joshtumath.uk/ Josh Tumath

    But how could I have Ember and Express running on the same server? I want the same server to be serving both a built Ember application and the RESTful API for that application.

    • Chirag Ravindra

      Assuming you are using Ember-CLI, run

      ember build –environment=”production”

      Then copy paste the contents of your /dist folder into the public folder in your express app. (Or any directory in the app and then configure the Express Static middleware to point to that folder)

      • Prashanth

        But when you deploy this app and when you reload on any route… it looks for node side of the route… and so it fails

      • Prashanth

        In other words… let’s say you have app.com/rentals and when you reload your app it looks for /rentals end point on your app

  • Satyam Tallam

    Hi got error after changed to {data:docs} from {notes:docs}, Please find the attachment

  • Satyam Tallam

    Error

    • Ben Smith

      I’m getting same error. Did you ever sort this out?

      • Ryan Christiani

        Hi, so I think the issue is that this post was written in April 2015, and a lot has changed in Ember and Ember Data things than. I am not 100% sure what you error might be, I am thinking the JSON coming from the server is different now. If you are using the latest Ember and Ember data I think the responses has to be {data: docs}, so change notes to data.

  • Chirag Ravindra

    Thanks for the post Ryan. Helped solidified my vague idea of an approach.

    Any idea how to handle related models on the server side? (Ember-Data manages it on the client side)

  • Ben Smith

    Seems like my Ember route is successfully GET requesting my node/express server. (In the terminal tab where I’m running node/express, GET /api/notes 200 is logged when I go to my Ember home page.) However, nothing actually shows up on the Ember home page. I don’t get an error saying the JSON isn’t formatted correctly, just a blank screen. Pretty sure my application.hbs is correct. Any ideas on what might be wrong?

    • Ben Smith

      Now I’m getting the following error: ember.debug.js:28535Error while processing route: index Assertion Failed: Encountered a resource object with an undefined type (resolved resource using frontend@serializer:application:) Error: Assertion Failed: Encountered a resource object with an undefined type (resolved resource using frontend@serializer:application:)

      The docs don’t make it easy to figure out how exactly the JSON should be formatted, which is where I think the problem is.

  • Matt

    Just to help keep this thing up to date (because it was pretty helpful to me). Following this exactly gave me no data and this warning: `WARNING: Encountered “0” in payload, but no model was found for model name “0” (resolved model name using appname@serializer:application:.modelNameFromPayloadKey(“0”))`. By switching from RESTSerializer to JSONSerializer:

    https://gist.github.com/alphastory/6a760ecbe21591cf2a7ebaf7c5cb508b

    I was able to retrieve and use my data. I’m user Ember 2.5.1 and Ember Data 2.6.1 with Mongo 4.5.1 and Express 4.14.0.

    Thanks for the awesome article!

    • Anil Kumar Malla

      Thank You much ! It really helped, by the way did u get why did it not work prior ?

  • Harxy

    Hey Ryan, thanks so much, been fighting with Ember for weeks now, but this finally got me through the wall and my Express server linked up to the front-end!

    Now I can only imagine it’s a matter of time until I’m a dotcom billionaire.

    • Matt Burton

      Hey man. Link up with me as I still have the same issues. http://www.facebook.com/burtonized

      • Harxy

        Sorry, I don’t have facebook… What’s your issue?

        • Matt Burton

          I can’t seem to do more advanced routes than ‘/’. Everything else seems to be blocked by express.

  • Amanda Stott

    Thanks! This was well written and easy to follow. Even though I am using a newer version of ember data, I was still able to get things up and running quickly, and I feel like I understand more about http now.

  • Милош Рајковић

    I have one question sir, after I finish all I cant get anything on http://localhost:4500/api/notes it writes : ”

    Cannot GET /api/notes”

    • http://derkhadim.github.io derkhadim

      You have to restart your node server

  • Stevan Vostic

    Thank you very much, this helped me a lot. For anyone that’s wondering if this code is up to date with the current Ember version (2.12.1), it is.

  • http://derkhadim.github.io derkhadim

    Great tutorial.

  • Prashanth

    How did you guys deploy a Ember Node App?
    Any examples please ?

  • Prashanth

    I’ve did ember build production and moved the prod folder to the node side.
    But how could I deploy this app on a linux box to get node running please ?

  • soclips

    Sorry..when I try to run node server, it gives me 491 error.

    module.js:491
    throw err;
    ^

    Error: Cannot find module ‘/Users/HelloWorldApp/server’
    at Function.Module._resolveFilename (module.js:489:15)
    at Function.Module._load (module.js:439:25)
    at Function.Module.runMain (module.js:609:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:598:3

    I think server is not available, but the steps don’t explain how to make it available?