Introduction to the MEAN Stack, Part Two: Building and Testing a To-do List

In last week’s blog post, I showed you how to install all of the basic tools that you need to get up and running with the MEAN Stack. Didn’t catch that one and need help getting started with the MEAN Stack? You can find everything you need in Introduction to the MEAN Stack, Part One.

This time we’re going to take that shiny new MEAN Stack and put it to good use by building a very simple to-do list. We’ll also set up a basic automated testing suite. This process will take 12 steps and you can follow along here. I will link to the diff on github for each step below as well. The mean-stack-todo repo started off as a direct duplicate of the mean-stack-skeleton repo from last week.

Over the course of this tutorial, you’ll see some of the key features and concepts that make the MEAN stack such a powerful development tool. Here are a few highlights to keep in mind:

  • The MEAN stack comes with a very powerful suite of testing tools. Javascript’s flexibility makes writing tests extremely easy, Jasmine syntax makes your tests into a set of stories that help lower the barrier to understanding your code base, and tools like Karma and Nodeunit enable you to have unit test coverage of your entire stack. Even if you decide to not write unit tests for your application, AngularJS’s end-to-end test runner enables you to fully automate your integration testing.
  • You can include dynamic Javascript directly into your templates. The first rule of writing AngularJS code is that your Javascript should never reference the DOM. In the AngularJS world, $(‘input#inputName’).val(‘vkarpov’) is (almost) always the wrong thing to do. Your Jade templates should be the definitive guide to all of your app’s UI/UX decisions, while your client Javascript should simply maintain the state of your data.
  • You use the same language all the way through. MongoDB stores your objects in a format that is very similar to JSON and the database shell uses Javascript-like syntax. NodeJS queries MongoDB using JSON queries, then passes the resulting objects to AngularJS as JSON objects. What does all this mean? All Javascript, all day. This streamlines the code itself and makes coordinating development across your team way easier.

1) Create some sample data for the server to display (diff)
Our to-dos are going to have a description, a due date, and a boolean flag to indicate whether or not they’re already done. Our app.js file shows that when the user accesses the GET / route, ExpressJS will call routes/index.js “index” function, which in turn renders views/index.jade. Let’s add a couple of sample to-dos to index.jade’s template parameters.

2) Modify the index.jade template to display our sample data using ExpressJS templates (diff)
Let’s create a very simple layout to display our to-dos. We’ll have to separate our to-dos into two lists: one for completed to-dos, and one for outstanding to-dos. It’s very important here to note that by default, ExpressJS uses Jade for templating. Jade is a controversial templating library which uses a simplified and more human-readable version of HTML syntax. For example, let’s say we have the following HTML:

<ul class="span6 text-center" id="tools-list">
	<li><a href="">AngularJS</a></li>
	<li><a href="">ExpressJS</a></li>

In Jade, this looks like:

      | AngularJS
      | ExpressJS

3) Add Bootstrap to our project using Bower (diff)
You’ll want to scroll all the way to the bottom of the diff, because this diff includes all of Bootstrap and jQuery. In addition to changes in the layout, you’ll notice that we take advantage of some of Jade’s inheritance features. The index.jade template inherits from layout.jade using the “extends layout” on the first line. In this commit, we added “block head” to layout.jade, and then in index.jade we overwrote “block head” to include bootstrap in our template.

As an aside, you’ll want to install bootstrap via

bower install bootstrap-css
bower-install bootstrap-javascript

because “bower install bootstrap” only gives you an un-compiled version of Bootstrap. This is pretty silly considering the fact that Twitter maintains both Bower and Bootstrap. Fate, it seems, is not without a sense of irony.

4) Use AngularJS to make our template dynamic (diff)
One big problem with our to-do list right now is that there is no way to move a to-do from the outstanding list to the completed list, or vice versa. A Jade template is compiled once and then served up as static HTML to the user. However, if we use AngularJS’s two-way data-binding as a templating tool, our template updates automatically to reflect the state on the client side. Once you’ve implemented this, items will automatically move to the completed list when you click the checkbox on the outstanding list.

5) Install AngularJS’s end-to-end test runner and write a simple test (diff)
One of AngularJS’s original co-authors was Misko Hevery, my former mentor at Google whose sole mission in the professional world is to make sure every piece of software has 100% test coverage. As you might expect, AngularJS was built with ease of automated testing as a core design consideration. Angular-scenario is a part of AngularJS that essentially hijacks an iframe to perform tests on your site as the user would see it. After this commit, run “node app.js” and navigate to http://localhost:3000/tests/e2e/runner.html. You should see something like this:


This page will run the test we defined in public/tests/e2e/scenarios.js. As you can see, Jasmine syntax does a pretty good job of making tests read like stories. Our test says when we navigate to “/,” we should see two to-dos in the outstanding list and one in the completed list. Then we click on a checkbox and expect that one of the to-dos moved to the completed list. You can install angular-scenario via bower

bower install angular-scenario

6) Write code for a REST-ful path to let the user add a to-do and a unit test (diff)
For this step, you need to install nodeunit with

npm install -g nodeunit

You can run the test suite with

nodeunit nodeunit/*

from the repo root directory. You’ll notice that the route is defined as a function that returns a function. The purpose of this is two-fold. First, it lets you write a proper unit test for this function. The core tenant of writing unit tests is that if you introduce a bug to a clean code base, the only tests that should fail are the ones that test the newly broken function. If the to-do objects were not passed to the route as a parameter, the route would have a hidden dependency that we wouldn’t be able to mock out, so bugs in the to-do object itself could potentially break the route’s test as well. This would be especially bad if to-dos were a complex object rather than a simple list. The secondary purpose for defining routes in the manner described above is that this syntax allows you to easily see which functions depend on a given module, which is very important if you have to change how a module works.

7) Open up the REST-ful path for adding a to-do (diff)
In line with the argument in step 6, we’ll move the todos array into app.js for now and inject it into our GET / and POST /todo.json routes. This pattern is called dependency injection.

8) Create a basic form to add a to-do (diff)
AngularJS gives us the ability to override the default form-submission action using the ng-submit directive, so we can perform the POST /todo.json action without refreshing the page by using AngularJS’s $http service.

9) Add angular-ui-bootstrap and use its datepicker (diff)
Fair warning, this commit is a little difficult to read because it includes all of angular-bootstrap. You can install it through bower using

bower install angular-bootstrap

One critical feature that was hardcoded into the previous commit was the due date of the new to-do. It was set to one week from now and there was no way to change it. Thankfully, the angular-bootstrap module includes, among other features, a nice datepicker directive. The hardest part is setting up an AngularJS module so that we can include angular-bootstrap. AngularJS uses modules to organize dependencies, so we need to create a TodoModule in public/javascripts/TodoModule.js and explicitly tell AngularJS that this module depends on the ui.bootstrap module (in addition to including the actual ui-bootstrap Javascript file). Once this is done, adding a datepicker input with two-way data-binding to a Javascript date becomes a trivial one-liner:


10) Set up a MongooseJS model (diff)
MongooseJS is a schema and validation tool for NodeJS and MongoDB. You use MongooseJS essentially the same way you would use an ORM in other development environments. Here we define a basic schema which tells MongooseJS the form that objects in the todos collection must take and define a Todo “model” which allows us to perform ORM-like operations on the todos collection. If you want to learn more about MongooseJS, I’ve written about it in much greater detail here and here.

11) Use the Todo model for database persistence and then fix our tests (diff)
Now that we have an object that lets us save and query a todos collection in MongoDB, we can make our GET / route query for all to-dos and our POST /todo.json route add a new to-do to the collection. Note that the function to save a new to-do can fail – if the posted object takes on an incorrect form (e.g. the “description” is empty), we’ll return an error object. For more information about handling this error object, you can read my post How to Easily Validate Any Form Ever Using AngularJS.

12) Tie up some loose ends: persist checking and un-checking, and reload data periodically (diff)
If you’ve been paying close attention, you may have noticed that up until now we missed a very important feature: we never actually update the backend when we mark a to-do as done. You would also be more observant than I was when I was outlining the steps for this post, because I missed that until I got to this step. However, like most of the tasks in this post, it is pretty trivial. We open up a PUT /todo/:id.json route that updates an existing to-do, create a function called “update” in TodoListController that uses $http to perform a PUT, and use the ng-change directive to call “update” every time the checked/unchecked status of a to-do changes.

Finally, we can add one more feature that I find tragically lacking in my personal to-do list app of choice, Todoist: periodic syncing with the server. I find it really frustrating to check off something as done on my laptop and then have to refresh the page on my Ubuntu box when I get back home. The simplest way to improve this is to add a dependency to AngularJS’s $timeout service and set it to ping the server for a full to-do list every 30 minutes.

[Corrected on 8/2/13 : Using $timeout causes the E2E test runner to hang until all outstanding calls to $timeout finish. This is a bug that the AngularJS team is currently working on. Until that’s fixed, I recommend avoiding using $timeout in favor of Javascript’s native setInterval (diff), or, better yet, your own custom AngularJS service that wraps setInterval. ]

Hey, guess what? You’ve just successfully built a to-do list app! How badass is that? What’s even better is that in some ways this fancy new app you built is in some ways more useful than commercial offerings like Todoist. [Note: To any Todoist team members who read this- I love many things about your software but hot damn there are some really annoying issues with it that I think could be improved]. Hope you found this little code adventure useful. By the way, I’ve been toying with the idea of building a more sophisticated to-do list/task management app in the near future that addresses a lot of the issues I’ve had working with Todoist, Wedoist, Asana, Jira, Github, Trello, Streak, etc. Would you guys be interested in something like this? Something along the lines of using Github to successfully coordinate tasks that aren’t actually code-related. What sort of features would you want to see in something like this? What annoys you about existing to-do or team management tools? Feel free to discuss in the comments section.

Have any questions about the code featured in this post? Want to suggest a better approach? Go ahead and leave a comment below, or tweet me at @code_barbarian. I’m always down for both unfettered praise and horribly vicious flame wars. Until then, feel free to rant about which task management apps you like and which make you want to feel like this in the comments. You know what’s better than a task management app? A guy like William Kelly (@idostartups). Major props to him as always for helping make this post happen.

45 thoughts on “Introduction to the MEAN Stack, Part Two: Building and Testing a To-do List

  1. Hi Valeri,
    Thanks for these awesome posts 🙂
    In section 5, you’re missing an image half-way through (where we “should see something like this:” Also, I’m getting 0 tests Passed/Failed/Errors when running the end-to-end tests after checking out the repo. The app works as described if I perform each of the steps in runner.html manually, but the runner itself stalls at the navigation to “/”. Likely something to do with my setup (versions perhaps) … I’ll let you know if I get to the bottom of it.

    • Hi Michael,

      Thanks for catching the missing image. Also good call on the hanging E2E tests, that’s my mistake, there’s a bug in the AngularJS E2E test runner that causes it to wait until all calls to $timeout are finished, which means that the E2E tests don’t run at all after step 12. My apologies for missing that, this is what I get for not running my own tests XD. I added a new commit to simply use setInterval instead ( This isn’t an ideal solution because you wouldn’t be able to test that using Karma, which is a unit test runner for AngularJS, but it’s good enough for now.

      Glad you’re enjoying the posts!


  2. Thanks a lot for the article and the previous. I’ll try to properly go through as soon as I have time. I really like the idea of using MEAN stack but I have a long way to go. Your tutorials are a great place to start so thanks!

    I actually like TodoIst and don’t find much wrong with it 🙂 I guess coming from Springpad it is pretty cool.

  3. I’m about to finish the tutorial, still I find that the date from the datepicker is not being passed to the POST, and today’s date is not being displayed, when the form is being loaded.

    When I clone your repo and compare it to mine, the only difference that I find is that the calendar is being drawn differently.
    When I inspect the calendar table element, I find that:
    class = ng-pristine ng-valid ng-valid-date ng-valid-date-disabled —> NOT WORKING
    class = well well-large ng-pristine ng-valid —> WORKING

    Everything else worked like a charm, but I still have that thorn of not being able to save the date….

    Cheers and thanks a lot for this great series of tutorials.

  4. Hi Valeri, wonderful post!

    However I have encountered a little problem. When i create a new todo item and set a different date… the datepicker does not pass the value on to the new todo item and all my new todos are defaulted to the +1 day “due” value. Is there something that I might have gotten wrong?

  5. in my opinion, it seems that there should be something different in the angular-bootstrap package, since the code I have tested is exactly the same as in the final version that is on github, that’s whay I think this is odd, since it’s working there…

  6. I qam stuck after step 4. I was expecting the index page should give me the items in the todos separated in updoming and done, 2 and 1 item respectively.
    But I am not getting anything. Just 3 blank headers To Do list, updcoming and Done. Nothing in between

  7. here is the diff I can observe when i run my step by step app and the completed mean-stack-todo
    abhishek@abhishek-3000-N100:~/mytestapp2$ node app.js
    Express server listening on port 3000
    GET / 200 1096ms – 1.49kb
    GET /stylesheets/style.css 304 6ms
    GET /javascripts/vendor/angular/angular.js 304 4ms
    GET /javascripts/controllers/TodoListController.js 304 4ms
    GET /javascripts/vendor/bootstrap-css/css/bootstrap.css 304 164ms
    ^Cabhishek@abhishek-3000-N100:~/mytestapp2$ cd ../mean-stack-todo/
    abhishek@abhishek-3000-N100:~/mean-stack-todo$ node app.js
    Express server listening on port 3000
    GET / 200 1640ms – 1.97kb
    GET /javascripts/TodoModule.js 304 3ms
    GET /javascripts/vendor/angular-bootstrap/ui-bootstrap-tpls.js 304 17ms
    GET /javascripts/controllers/TodoListController.js 200 19ms – 1.21kb
    GET /javascripts/vendor/bootstrap-css/css/bootstrap.css 200 28ms – 124.37kb
    GET /stylesheets/style.css 200 52ms – 110b
    GET /javascripts/vendor/angular/angular.js 200 79ms – 481.87kb
    GET /todos.json 200 9ms – 347b
    GET /javascripts/vendor/bootstrap-css/img/glyphicons-halflings.png 304 2ms

    As I expected for style.css angular.js TodoListController.js bootstrap.css all are giving me 304
    So, I guess I am missing some config

  8. Well, excuse my earlier comments. #304 was just an oversight, got confused with 404. But definitly there something I am missing or overlooking with angular – express integration, can you guys please help.

    On google I have seen other resources for the help too. all what is required is saying ng-controller and ng-init (though there I can try setting todo = instead of calling settodos , i havent tried this) and then there are ways to specify that in app.js only instead of routes/index.js
    Would that make a difference? I am not sure about exports too.

    I am new to MEAN, so might be I am doing something silly but I guess no question is silly question.

  9. Very interestings posts, perfect because I want to enter in nodeJs for our projects.

    Do you think MEAN is a good environment for business development?(ERP,CRM, etc..)

    There are some important difference between this and

  10. Thanks for the tutorial, it was definitely very helpful in trying to get up to speed. I’m wondering if you know what it would take, or if it’s even possible to remove jade from the project entirely? I want to continue to use html5 and learn angular without jade. I want to extend your tutorial app a bit more to learn the internals and externals of mongo/ose, angular and express.

    I tried simply re-writing the index.jade and layout.jade into one index.html file and removing some jade references, then doing a node uninstall jade, but it seems to be ‘built-in’ pretty heavily. Do you know if there’s an easy way to get it running without jade?

  11. Hey Valeri,
    thank you for that excellent introduction to the MEAN stack.

    I have one question about it.
    Is there an easy way to share server-side and client-side model validation? That would be awesome, so we don’t have to make a request if it’s not valid!

  12. Hi Valeri,
    Awesome article! Extremely helpful. I have a question regarding the templating. You mention above that your jade templates should be the guide for UI/UX decisions. My question is, what types of templates do we template from the server, and what should be templates on the client? In other words where should we use jade and where should we use HTML in the public folder?

  13. Hi Valeri,
    Awesome article! Extremely helpful. I have a question about the templating. You mentioned above that the jade templates should be your guide for UI/UX. My question is when should I use the server-side templating and when should I be using client side? In other words what should I use jade templates for and what should I use the HTML in the public folder for?

    Thanks again for the great post,
    Tom M

    • Hi Tom,

      Glad you enjoyed the article. The answer to your question is that you should use Angular for templating as much as possible for flexibility. However, HTML in the public folder should generally be for truly static code. Putting that code in a route gives you more fine grained control with logging and keeping HTML in memory, whereas you need some extra configuration to make Express do anything other than just read the HTML directly from disk if you serve up your HTML directly from public/. Hope this helps.

  14. Hi Valery, I’m stuck on step 5 – where we should be able to see the test runner. In fact, I see the header “AngularJS Scenario Test-Runner”, but I don’t see the todo list… Any idea folks ?

    Sorry, I’m a beginner with Mean… I figured out to complete all the tutorial and everything is working apart of the test runner 😦 Which is the part I was looking for as a QA guy 😉

  15. nice job!!…I hope one day you can do an article how to deploy an application with the “Mean stack”. I tried to do with this app using MongoDB and Heroku, but I’m having problems 😦

    Finally, thank you very much for the tutorial, it was a great help for me and running all perfectly.

    • You should be able to simply create another function, e.g.

      function OtherController($scope) {
      // Stuff

      And then use `ng-controller=”OtherController”` in your HTML

  16. You actually make it appear so easy together with your presentation however I to find this
    topic to be actually one thing which I feel I’d never
    understand. It seems too complex and extremely wide for me.

    I’m looking forward to your subsequent publish, I’ll
    try to get the cling of it!

  17. @vkarpov15 [ With tests, NICE ! hehe ]
    Thank you !
    On my setup ( ubuntu-14.04 based – chrome )
    I had to move the datepicker out of the form because the arrows to change month were submiting the form.
    I forked but I dunno if everyone had this problem, so I didn’t make a PR.

    Well, thank you again ! ! !

  18. Great post! Would you ever consider revamping this tutorial so that it covers the latest version of Node? Some things have changed in terms of syntax and it’s difficult to follow along when these versions are incompatible. Thanks!

    • Do you mean Node 0.11.x and ES6 syntax, like yield and such? Probably not in the near future: Node 0.11 is unstable, and ES6 support on browsers is spotty, meaning your code sharing options are limited even if you’re using node 0.11.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s