Creating a Single Page Todo App with Node and Angular

Single Page Application with Node and Angular

Today we will be creating a very simple Todo application using the MEAN (Mongo, Express, Angular, Node) stack. We will be creating:

  • Single page application to create and finish todos
  • Storing todos in a MongoDB using Mongoose
  • Using the Express framework
  • Creating a RESTful Node API
  • Using Angular for the frontend and to access the API
This article has been updated for ExpressJS 4.0.

While the application is simple and beginner to intermediate level in its own right, the concepts here can apply to much more advanced apps. The biggest things we should focus on is using Node as an API and Angular as the frontend. Making them work together can be a bit confusing so this tutorial should help alleviate some confusion. Buckle those seatbelts; this could be a long one.

What We’ll Be Building


Base Setup

File Structure

We are going to keep the file structure very simple and put most of the code for our Node application into the server.js file. In larger applications, this should be broken down further to separate duties. is a good boilerplate to see best practices and how to separate file structure. Let’s go ahead and create our simpler file structure and edit the files as we go along.

	- public 			<!-- holds all our files for our frontend angular application -->
	----- core.js 		<!-- all angular code for our app -->
	----- index.html 	<!-- main view -->
	- package.json 		<!-- npm configuration to install dependencies/modules -->
	- server.js 		<!-- Node configuration -->

Installing Modules

In Node, the package.json file holds the configuration for our app. Node’s package manager (npm) will use this to install any dependencies or modules that we are going to use. In our case, we will be using Express (popular Node framework) and Mongoose (object modeling for MongoDB).

  "name"         : "node-todo",
  "version"      : "0.0.0",
  "description"  : "Simple todo application.",
  "main"         : "server.js",
  "author"       : "Scotch",
  "dependencies" : {
    "express"    : "~4.7.2",
    "mongoose"   : "~3.6.2",
    "morgan"	 : "~1.2.2",
	"body-parser": "~1.5.2",
	"method-override": "~2.1.2"

Now if we run npm install, npm will look at this file and install Express and Mongoose.


Node Configuration

In our package.json file, we told it that our main file would be server.js. This is the main file for our Node app and where we will configure the entire application.

This is the file where we will:

  • Configure our application
  • Connect to our database
  • Create our Mongoose models
  • Define routes for our RESTful API
  • Define routes for our frontend Angular application
  • Set the app to listen on a port so we can view it in our browser

For now, we will just configure the app for Express, our MongoDB database, and listening on a port.

// server.js

	// set up ========================
	var express  = require('express');
	var app      = express(); 								// create our app w/ express
	var mongoose = require('mongoose'); 					// mongoose for mongodb
	var morgan = require('morgan'); 			// log requests to the console (express4)
	var bodyParser = require('body-parser'); 	// pull information from HTML POST (express4)
	var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)

	// configuration =================

	mongoose.connect('mongodb://'); 	// connect to mongoDB database on

	app.use(express.static(__dirname + '/public')); 				// set the static files location /public/img will be /img for users
	app.use(morgan('dev')); 										// log every request to the console
	app.use(bodyParser.urlencoded({'extended':'true'})); 			// parse application/x-www-form-urlencoded
	app.use(bodyParser.json()); 									// parse application/json
	app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json

	// listen (start app with node server.js) ======================================
	console.log("App listening on port 8080");

Just with that bit of code, we now have an HTTP server courtesy of Node. We have also created an app with Express and now have access to many benefits of it. In our app.configure section, we are using express modules to add more functionality to our application.

Database Setup

We will be using a remote database hosted on They provide a great service and give you $15 upfront to use as you see fit. This is great for doing testing and creating databases on the fly.

Modulus will provide the database URL you need and you can use mongoose.connect to connect to it. That’s it.

Start Your App!

Now that we have our package.json and server.js started up, we can start up our server and see what’s going on. Just go into your console and use the following command:

node server.js

Now you have a server listening on port 8080. You can’t see anything in your browser at http://localhost:8080 yet since we didn’t configure our application to output anything. But it’s a start!

Automatically restart server when files change: By default, node will not monitor for file changes after your server has been started. This means you’d have to shut down and start the server every time you made a file change. This can be fixed with nodemon. To use: install nodemon globally npm install -g nodemon. Start your server with nodemon server.js now. Smooth sailing from there.

Application Flow

Now a brief overview of how all our moving parts will work together. There are a lot of different ideas and technologies involved in this application that it is easy to get mixed up with them all. In our diagram below, we explain a bit of the separation of tasks and how the parts tie in together.


Angular is on its own in the frontend. It accesses all the data it needs through the Node API. Node hits the database and returns JSON information to Angular based on the RESTful routing.

This way, you can separate the frontend application from the actual API. If you want to extend the API, you can always build more routes and functions into it without affecting the frontend Angular application. This way you can eventually build different apps on different platforms since you just have to hit the API.

Creating Our Node API

Before we get to the frontend application, we need to create our RESTful API. This will allow us to have an api that will get all todos, create a todo, and complete and delete a todo. It will return all this information in JSON format.

Todo Model

We must define our model for our Todos. We’ll keep this simple. After the configuration section and before the listen section, we’ll add our model.

	// define model =================
	var Todo = mongoose.model('Todo', {
		text : String

That is all we want. Just the text for the todo. MongoDB will automatically generate an _id for each todo that we create also.

RESTful API Routes

Let’s generate our Express routes to handle our API calls.

// server.js

// routes ======================================================================

	// api ---------------------------------------------------------------------
	// get all todos
	app.get('/api/todos', function(req, res) {

		// use mongoose to get all todos in the database
		Todo.find(function(err, todos) {

			// if there is an error retrieving, send the error. nothing after res.send(err) will execute
			if (err)

			res.json(todos); // return all todos in JSON format

	// create todo and send back all todos after creation'/api/todos', function(req, res) {

		// create a todo, information comes from AJAX request from Angular
			text : req.body.text,
			done : false
		}, function(err, todo) {
			if (err)

			// get and return all the todos after you create another
			Todo.find(function(err, todos) {
				if (err)


	// delete a todo
	app.delete('/api/todos/:todo_id', function(req, res) {
			_id : req.params.todo_id
		}, function(err, todo) {
			if (err)

			// get and return all the todos after you create another
			Todo.find(function(err, todos) {
				if (err)


Based on these routes, we’ve built a table to explain how a frontend application should request data from the API.

HTTP Verb URL Description
GET /api/todos Get all of the todos
POST /api/todos Create a single todo
DELETE /api/todos/:todo_id Delete a single todo

Inside of each of our API routes, we use the Mongoose actions to help us interact with our database. We created our Model earlier with var Todo = mongoose.model and now we can use that to find, create, and remove. There are many more things you can do and I would suggest looking at the official docs to learn more.

Our API is done! Rejoice! If you start up your application, you can interact with it at localhost:8080/api/todos to get all the todos. There won’t be anything currently since you haven’t added any.

Frontend Application with Angular

We have created a Node application, configured our database, generated our API routes, and started a server. So much already done and still a little bit longer to go!

The work that we’ve done so far can stand on its own as an application. It can be an API we use let applications and users connect with our content.

We want to be the first to use our brand new API that we’ve just created. This is one of my favorite terms that I learned about last month: We will be dogfooding. We could treat this as we are our very first client to use our new API. We are going to keep this simple so we’ll have just our index.html and core.js to define our frontend.

Defining Frontend Route

We have already defined our API routes. Our application’s API is accessible from /api/todos, but what about our frontend? How do we display the index.html file at our home page?

We will add one route to our server.js file for the frontend application. This is all we need to do since Angular will be making a single page application and handle the routing.

After our API routes, and before the app.listen, add this route:

// server.js
	// application -------------------------------------------------------------
	app.get('*', function(req, res) {
		res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)

This will load our single index.html file when we hit localhost:8080.

Setting Up Angular core.js

Let’s go through our Angular setup first. We have to create a module, create a controller, and define functions to handle todos. Then we can apply to view.

// public/core.js
var scotchTodo = angular.module('scotchTodo', []);

function mainController($scope, $http) {
	$scope.formData = {};

	// when landing on the page, get all todos and show them
		.success(function(data) {
			$scope.todos = data;
		.error(function(data) {
			console.log('Error: ' + data);

	// when submitting the add form, send the text to the node API
	$scope.createTodo = function() {
		$'/api/todos', $scope.formData)
			.success(function(data) {
				$scope.formData = {}; // clear the form so our user is ready to enter another
				$scope.todos = data;
			.error(function(data) {
				console.log('Error: ' + data);

	// delete a todo after checking it
	$scope.deleteTodo = function(id) {
		$http.delete('/api/todos/' + id)
			.success(function(data) {
				$scope.todos = data;
			.error(function(data) {
				console.log('Error: ' + data);


We create our Angular module (scotchApp) and controller (mainController).

We also create our functions to get all todos, create a todo, and delete a todo. All these will be hitting the API we just created. On page load, we will GET /api/todos and bind the JSON we receive from the API to $scope.todos. We will then loop over these in our view to make our todos.

We’ll follow a similar pattern for creating and deleting. Run our action, remake our todos list.

Frontend View index.html

Here we will keep it simple. This is the HTML needed to interact with Angular. We will:

  • Assign Angular module and controller
  • Initialize the page by getting all todos
  • Loop over the todos
  • Have a form to create todos
  • Delete todos when they are checked
<!-- index.html -->
<!doctype html>

<html ng-app="scotchTodo">
	<!-- META -->
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

	<title>Node/Angular Todo App</title>

	<!-- SCROLLS -->
	<link rel="stylesheet" href="//"><!-- load bootstrap -->
		html 					{ overflow-y:scroll; }
		body 					{ padding-top:50px; }
		#todo-list 				{ margin-bottom:30px; }

	<!-- SPELLS -->
	<script src="//"></script><!-- load jquery -->
	<script src="//"></script><!-- load angular -->
	<script src="core.js"></script>

<body ng-controller="mainController">
	<div class="container">

		<div class="jumbotron text-center">
			<h1>I'm a Todo-aholic <span class="label label-info">{{ todos.length }}</span></h1>

		<!-- TODO LIST -->
		<div id="todo-list" class="row">
			<div class="col-sm-4 col-sm-offset-4">

				<!-- LOOP OVER THE TODOS IN $scope.todos -->
				<div class="checkbox" ng-repeat="todo in todos">
						<input type="checkbox" ng-click="deleteTodo(todo._id)"> {{ todo.text }}


		<div id="todo-form" class="row">
			<div class="col-sm-8 col-sm-offset-2 text-center">
					<div class="form-group">

						<!-- BIND THIS VALUE TO formData.text IN ANGULAR -->
						<input type="text" class="form-control input-lg text-center" placeholder="I want to buy a puppy that will love me forever" ng-model="formData.text">

					<!-- createToDo() WILL CREATE NEW TODOS -->
					<button type="submit" class="btn btn-primary btn-lg" ng-click="createTodo()">Add</button>



Take a look at what we have.



Now we have a fully working application that will show, create, and delete todos all via API (that we built!). That was quite a day. We’ve done so much. Just an overview of what we’ve accomplished:

  • RESTful Node API using Express
  • MongoDB interaction using mongoose
  • Angular AJAX $http calls
  • Single page application w/ no refreshes
  • Dogfooding (sorry, I really like that word)

Test the Application

Go ahead and download the code on Github and tweak it or test it. To get it all up and running:

  1. Make sure you have Node and npm installed
  2. Clone the repo: git clone [email protected]:scotch-io/node-todo
  3. Install the application: npm install
  4. Start the server: node server.js
  5. View in your browser at http://localhost:8080

I hope this was insightful on how to have lots of moving parts work together. In the future, we will look at separating our server.js file since that got a little crazy.

Further Reading

If you are interested in more MEAN stack applications, we’ve written up a guide to get you started in building your own MEAN stack foundation.

Setting Up a MEAN Stack Single Page Application

Edit #1: Removing ng-init
This article is part of our Node and Angular To-Do App series.

  1. Creating a Single Page To-do App with Node and Angular
  2. Node Application Organization and Structure
  3. Angular Modules: Controllers and Services

Chris Sevilleja

Design, development, and anything in between that I find interesting.

View My Articles

Stay Connected With Us
hover these for magic

Get valuable tips, articles, and resources straight to your inbox. Every Tuesday.

  • Dave Woodall

    Thanks for writing!

    • Chris Sevilleja

      You’re welcome. Always helps my learning process to write blog about it. Are there any other tutorials you’d like to see on Node or Angular?

      • Thomas Stock

        I’d like to see one on Node+Angular authentication.
        Registering, login/logout, Social auth.
        You could extend this ToDo app to work with registered users.

        • Chris Sevilleja

          Good idea. I’ve been doing research on using Passport for local and social logins. Once I get it all figured out, I’ll write an article on it. Lookout for that next week.

          • Sonicrida

            I’ll be looking forward to this!

  • Thomas Stock

    Thanks a lot, this was very educational and well written.

    • Chris Sevilleja

      Thanks for reading!

  • Pingback: Links for 8th November | Web Developer Essentials

  • petersirka

    I created a similar app in node.js:

    • Chris Sevilleja

      Very cool. Thanks for sharing.

  • visionpoint

    Good stuff. Learned quite a lot through this tutorial and already look forward to that next article on using Passport for auth.

    • Chris Sevilleja

      Good to hear. I’m going to be making an entire series on this. Next is auth. Then Then whatever else comes up.

      • TeddyJenkins

        that would be super awesome. this is one of the best ANGULAR tuts out and believe me i’ve SEARCHED

        • Chris Sevilleja

          Thanks for the validation! I was definitely in the same boat. Couldn’t find any really solid tuts for implementation and the Angular docs could be much better. Might as well make tuts as I learn!

      • visionpoint

        Chris, sounds solid! I think you definitely should go ahead and write some more tutorials as you learn — sure it’ll help a lot of devs out there trying to get into Angular, myself included. BTW, have you tried out this app with Angular 1.2? Deleting todos doesn’t work. I think this has to do with this change: but I haven’t figured out how to fix it yet. Any ideas?

        • Chris Sevilleja

          Glad to hear they help. Just released the next one ( for app organization and file structure. It’ll help move our app in the right direction.

          As for 1.2, I haven’t quite figured out what the issue is yet. There’s a lot of gobbledygook to run through on that page you linked. Thanks for the link btw, that will definitely help.

          I’ll keep you posted.

          • visionpoint

            Actually, just figured out what the issue was. It turns out that one of the breaking changes in Angular 1.2.0-rc2 (currently latest stable build) is the introduction of private properties on the scope chain. This potentially breaks a lot of apps that store data in document-oriented databases such as MongoDB. For now, I’m using version 1.2-rc3, which is the previous build version and hadn’t introduced this change yet. But the Angular changelog mentions that sensitive APIs should be wrapped in a closure/controller instead (link: Lots of people out there with broken apps because of this change.

            Excited about the tut on file organisation, btw. Will check that one out soon.

          • Chris Sevilleja

            Good find! That’ll definitely help fix up the to do app. Thanks for the info. I was debating on doing an “Upgrade your Angular App from 1.0.8 to 1.2″ article so this info can be spread to the world. (I’ll be sure to credit you for the work!)

          • visionpoint

            Sounds great Chris! I’m interested in this new suggested approach I mentioned in my previous post, so if I put some code together to cover that before you release your Upgrade to 1.2 article, I’ll let you know.

          • Chris Sevilleja

            Awesome thanks! That’ll probably be late next week. I have a couple Angular/Node articles to do before I get back to the To Do app.

  • occulti

    Amazing tutorial ! Thumbs up !

  • ackzell

    Great tutorial Chris! I have a question though: What if I want to load bootstrap, jquery and / or angular from a local file? All I get is a 404 or my sources get the contents of “index.html” overwritten into them.

    • Chris Sevilleja

      Hey ackzell. If you create a /public/js/jquery.js file. You can load that normally by using in index.html.

      • ackzell

        Thanks for your quick response Chris,
        The “public” folder is what did the trick. The thing is, I tried using bower for the dependencies, but it created a “bower_components” folder outside the “public” folder. Moving the files into the public folder allowed me to load the libraries without hassle.
        Thanks again!

        • Chris Sevilleja

          Great! Bower is awesome. Glad you got it working.

          • Florin

            Same issue here. What is exactly happening? I tried adding a new route for the js files, like this: app.use(express.static(__dirname + ‘/bower_components/jsdependency/lib.js’)); But it’s not working.. Any suggestions why?

          • Chris Sevilleja

            So I just tested this. If you are using bower, you’ll want to make that line:

            app.use(express.static(__dirname + ‘/bower_components’));

            You are specifying a directory, not linking directly to a file. This means that all the files that your app looks for will be in that directory. So in your index.html file, you can link to let’s say jquery with:

            The file structure for me when installing jquery through bower is


            I hope that helps.

    • ncerminara

      To add to Chris’s comment, if you’re running off a web server, referencing a file this way “//” will match the current protocol if it’s HTTP or HTTPS. If you are running locally off your desktop (without Vagrant, WAMP, XAMP, etc), you’ll need the full path “ or“. Without the protocol, your desktop is looking for that locally — as in on your computer.

      • ackzell

        thanks a lot for your answer ncerminara, I knew about the “//” matching the protocol (I learned that from Paul Irish on html5Boilerplate), but I also wanted to load a locally hosted set of files (just as a proof of concept)

  • Steven Waters

    Hey this tutorial is great so far, but I would review the first code block under the “Node Configuration” headline. It almost looks like malicious code has been injected into it. It starts with CDATA. At first, I tried to run my app with this code in it but it threw errors.

    • Chris Sevilleja

      Sorry about that. It’s been fixed. Thanks!

      • Steven Waters

        You’re welcome! Btw, nice tutorial!

  • Deacon

    Excellent tutorial – thanks!

    • Chris Sevilleja

      You’re welcome! Thanks for reading.

  • Lazaro Prates Junior

    I liked a lot. Congratulations

  • luongvancong

    Nice post! Thanks!

  • Jesus Rodriguez

    Hey Chris, awesome page, I love the design. I came here to read about node because I want to learn it. I checked this serie to see how you integrate Angular.js into Node.

    I am an Angular.js developer/author and here are my thoughts.

    A controller should exist inside a module, not outside. I know that some tutorials do that, but there is no reason to leave a controller outside a module (I saw that you fix that in a later article).

    The problem so far is the:


    I can understand that you want to use jQuery (but maybe there is no need for a todo app) but you shouldn’t manipulate your DOM inside a controller, not even for the sake of a tutorial. That is a really really bad practice that everyone should avoid. There is an easy way to clean your input:

    $scope.formData.text = “”;

    Since the input is two-way binded to that primitive, you can assign an empty string to remove the text of the input.

    Other than that, good tutorial, I’ll keep reading it :)

    • Chris Sevilleja

      Hey Jesus, thank you very much for your input. You are completely right and I realized my mistake just this past week when I was working on the other to-do tutorials.
      The current code for the demo utilizes $scope.formData.text = “”; but I haven’t updated the tutorial. I’ll definitely point it out to make sure that people understand that’s the best way to do it. Thanks again.

      • Jo Rafali

        Hi Chris,
        The GH repo seems to be an older version of the code as this line is still in there.
        Actually I must thank Jesus: I spent a bit of time trying to figure out what this was all about as I’m new to Web Dev and not familiar with JQuery, until I read his comment and realised the code isn’t the same on the repo.

        Btw, thanks for the great tutorials, extremely helpful and to the point!

        • Chris Sevilleja

          Sorry about that. Good call. The repo has been updated with the correct code. Thanks for pointing that out and thanks for reading!

  • Pingback: 4goodapp | Pearltrees

  • Connor James Leech

    How did this code:

    `mongoose.connect(‘mongodb://node:[email protected]:27017/uwO3mypu’);`

    get generated?

    • Chris Sevilleja

      If you create an account on or and create a database there, they will give you the code to connect to the newly created database.

      • Connor James Leech

        I ran

        $ modulus login –github

        $ modulus deploy

        and got the code “angularexpress-[somenumber]”

        Where does the “node:node@” and “:27017/uwO3mypucome” from in:

        mongodb://node:[email protected]:27017/uwO3mypu

        • Chris Sevilleja

          node:node is the : that I set up on the modulus website when I created the database.

          If you login, go to databases, go to administration, you can grab the full URL to connect to the database.

  • teressa

    I connected to the database using modulus by changing to my details, and made a DB on modulus called todo. Have a look here: but it doesnt add any todo’s / show any ?

    • Chris Sevilleja

      Can you connect to your database using Robomongo? Also when you retrieve the todos in your core.js file, try adding console.log(data); after line 9 and see if anything gets output.

  • yolo

    Thanks for a lot. And how to make code highlighter like this page? syntaxhighlighter?

    • Chris Sevilleja

      We use Prism JS

  • goldesigner

    Hey Chris, I am a hobbyist and I think your tutorials are fantastic! You have a knack for simplifying complex things so they are easy to grasp.

    Two questions for you:
    1. When I launch localhost:3000 to load the index.html page, I don’t see a GET request in my Terminal for “/” or anything. When the controllers load I see a GET request for /api/todos. So why is the initial GET from the browser to node not logged?
    2. Your website doesn’t have any direct means to contact you. How does one contact you for a Web Dev project other than through LinkedIn, Google+ or other social sites? Or is that your preferred mode of client introduction?

  • Sly Cooper

    Those…social network…buttons…hnng.

    • Shawn

      I wanted to say something about them too.. but hey.. they got our attention.

  • seb

    Awesome tutorial, keep up the great work!

  • Brad Traversy

    Chris this is hands down the best node tutorial I have read and Ive read alot latley. I couldnt wrap my head around interacting with the REST api with angular and you made it so easy. Thanks!

    • Chris Sevilleja

      Awesome! Glad to hear it helped.

  • akagr

    Nice tutorial. Thanks!

  • Pingback: Setting Up a MEAN Stack Single Page Application ♥ Scotch

  • Bohdan Lev

    How to receive data from few input fields using this example?

    • Chris Sevilleja

      You can get data from a POST call on the Node side using Just bind it to a variable in Angular using ng-model and have that passed through your POST.

      • Bohdan Lev

        Thank you! Works perfect)

  • Sam

    doing that:

    app.get(‘*’, function(req, res) {
    res.sendfile(‘./public/index.html’); // load the single view file (angular will handle the page changes on the front-end)

    is going to redirect ALL GET requests to index.html. But that is causing issues because when I try to load my angular app.js file, it serves index.html instead.

    How do I prevent real files (.js, .jpeg, etc…) from being redirected to index.html ?

    • Sam

      got it. that’s the purpose of app.use(express.static(config.root + ‘/public’))

      • CodySCarlson

        @Sam, Thx for the input but what is “config.root”? Node throws an error as it cannot find “config”.

  • tobbbe

    How/why is the collection in the database named “todos”? When I define the model I name it “Todo” (mongoose.model(‘Todo’,… ). Is it the route that does it (/api/todos)?

    Also, what is this for: app.use(express.methodOverride()); ?

    Thanks for a grrrrrrrrrrrreat tutorial!

    • Chris Sevilleja

      The collection in the database took the name of the model. The route can be named anything you want, but we just made it todos for a coherent example.

      methodOverride is so that express can spoof the HTTP verb. It gives it the ability to create PUT/PATCH and DELETE HTTP calls.

      • tobbbe

        But we named the model “Todo”? Is it always renamed to all lowercase and adds an s at the end?

  • Nikola Kochovski

    Do I have to define routes for every view on the server side?
    I have defined them on angular’s side, but not on the server side and I have a problem for one of the views

    • Chris Sevilleja

      No. The routing for them will be handled on the frontend site. You just need to define one route on the backend.

      // application ————————————————————-
      app.get(‘*’, function(req, res) {
      res.sendfile(‘./public/index.html’); // load the single view file (angular will handle the page changes on the front-end)

      This is the Node catch-all route that will send all requests to the Angular routing.

  • Pingback: jbzscmngbvsbbfbghv

  • Pingback: To do app with Node / Angular | My Dev Note

  • Joco

    I really like the tutorial, it’s simple and it gives a good idea how to start doing something real… One question though, I find it a little slow, what could make it happens?

    • Chris Sevilleja

      There are many different things that could affect speed. It could be the API endpoint. It could also be the time it takes to access the data from the database.

  • Pingback: Creating a Single Page Todo App with Node and Angular ♥ Scotch | Angular JS learning, tutorials, examples, tips

  • vjennings

    wonderful tutorial!! looking forward to more of these!

    how is angular identifying the data coming from mongodb? where are we telling angular that each ‘todo’ corresponds to an instance in the ‘Todo’ model? does it bind to them by name (i.e. does it say that todo == Todo and binds accordingly?) suppose we had another model in the database called ‘Modo,’ would we have to call the data ‘modo’ in angular so that it knows to bind to it on the server-side?

    thanks again for all your effort! this is one of the highest quality tutorials i’ve seen!

    • Chris Sevilleja

      Hello. Angular doesn’t identify the data coming from mongo. Node does that. In our Node backend, when we defined our Mongoose model, Mongoose will automatically look for a todo in the database. We then return that data to Angular when Angular requests it using the $http.get.

      All Angular knows is the URL to use when getting data (/api/todos) and that it’s being returned data.

  • Pingback: Google

  • jaguilera

    Thank u, it’s been very helpful for me

  • Steve

    Thanks for the great tutorial! One thing I think is missing, that I had to add, is in the core.js:

    var scotchTodo = angular.module(‘scotchTodo’, []);

    // hook the controller into the app
    scotchTodo.controller(‘mainController’, ['$scope', '$http', function($scope, $http) {

    ... controller code ...


    Also, in the routes, I did this instead:

    app.get(‘/’, function (req, res) {

    app.get(‘*’, function (req, res) {

    In this way, if you browse to http://localhost:8080/abc you will be redirected to / and index.html will be served. The way it is in the tutorial, the index.html is served, but the trailing abc is left in the url.

    Again, thanks very much for the great tutorial. I will definitely be working my way through the others shortly, and reading many of the other valuable posts on this site!

  • Vinodh

    Been looking all over for a angular – express – node tutorial , and this was one of the best. Thanks a lot!

  • Yusup

    Amazing post Chris, Thanks. You could have added services to the AngularJS part though.

  • user1

    Resource interpreted as Script but transferred with MIME type text/html: “http://localhost:8080/ToDo/public/js/core.js”.

    and browser throws following errors in this example

    Uncaught SyntaxError: Unexpected token < core.js:2

    Uncaught Error: No module: scotchTodo angular.min.js:18

    • CodySCarlson

      @user1, Exactly (arrrrg!)… The reason for this is that when you ask the server for “core.js” (along with *anything else), the server sees a ‘/…’ (‘/core.js’ for your request) and still serves the ‘index.html’ file. The promise in Express says “app.get(‘*’, …);” — mind the *all character — this is the same as “app.get(‘/*’, …);”. Meaning, ALL GET requests — including requests of ‘/api/…’ GETs (though route ordering may affect this). The only way I can see getting around this is to namespace your app.get to ‘app.get(‘/namespace/*’, …);’ AND have all your Angular Routes begin with this: e.g.: ‘.when(‘/namespace/r/o/u/t/e’, { … })’.

      I just can’t see a way to only ‘redirect all’ for only those requests which are not for other assets (js, css, img, etc).


      • Eric Swann

        I wouldn’t call it elegant, but I just put a route in front of the catchall to serve the script specifically. So when getting core.js, it hits that route first and stops. Ex:
        app.get(“/public/core.js”, function (req, res) {
        app.get(“*”, function(req, res) {

  • Adam C

    Excellent tutorial! Really, this taught me a lot. Keep up the good work.

  • nicholasrowe

    Is the example working? :D

  • acoyfellow

    Can we see a continuation of this tutorial, on how to secure the API?

    • Chris Sevilleja

      Absolutely. I have been meaning to extend this for some time, just got carried away with the other topics. I’m getting back into the MEAN stack and will release a lot more articles soon.

  • Pingback: [Angularjs] AngularJS-Learning | Jinny's Blog

  • Skuli Oskarsson

    The Demo is not working

  • samiyuru

    great tute. thanx

  • Pingback: Retrieving and Displaying Data with AngularJS and the MEAN Stack: Part II | Programmatic Ponderings

  • bedoui

    it’s possible to share a project about comment-user based only on node.js not php

  • Pingback: Create A Single Page TODO App with MEAN | kai

  • swapnesh sinha

    What does done: false stands for in the code -

    text : req.body.text,
    done : false
    }, function(err, todo) {
    if (err)

    // get and return all the todos after you create another
    Todo.find(function(err, todos) {
    if (err)

  • Anders

    This is great, good job!

  • Elodie I

    Thanks for this tutorial!! A pretty good one!

  • justin

    In createTodo instead of $scope.todos = data; you should have $scope.todos.push(data)

    • oldroy

      And in $scope.deleteTodo add index
      $scope.deleteTodo – function(id,index){

      and in index.html change the deleteTodo click…. ng-click=”deleteTodo(todo._id, $index)”> …etc.
      $index is apparently provided for each item in arrays within ng-repeat….
      gets rid of deleted todo in angular on success from node/mongodb

  • Amin Ogarrio


  • Mizael Galvez

    thanks man !!! a great job for a good guide !!!! I will be quite useful !!!

  • Alberto Villalobos

    Very readable and well written! I like starting from scratch when learning new things, I tried going straight to the mean-fullstack generator but I had a lot of missing knowledge, now things make much more sense.

  • Yasar Hussain

    how would you go about allowing multiple users to create a todo list? right now it creates one that everyone can edit but i think it’d be more useful to have it so each person has their own todo list

  • Devon Noel de Tilly

    I think you might want to include “done : Boolean” in the Todo model.

  • Pingback: AngularJS-Learning | Nisar Khan

  • Jonas H.

    Great tutorial! Very readable and with working code. And dogfooding is a great term :)

    • Chris Sevilleja

      Agreed. I’m spreading it as much as I can.

  • Oron Ben Zvi

    Hello again Chris, just a comment:


    Isn’t relevant here since you use $http.delete explicitly.

    • Chris Sevilleja

      Yes of course. That was a silly addition. I’ll remove it.

  • Llewellyn Collins

    Very Nice. Thanks

  • Canming Jiang

    The code seems not working… Whenever I opened http://localhost:8080/, it always gave me “No data received”…

  • Lars

    Thank you for a great and fun tutorial. Took me about 20 minutes, gave a great overview!

  • Raz

    Online demo and code from github are broken. The todos are not displayed in the SPA and new notes are not added to the DB. Route is working as adding api/todos to the URL is returning document(s) from the DB collection.

    • Raz

      Modified todoService (todos.js) to use ‘/api/todos’ as well, same as in the routes. Works now!

  • Pingback: I need a Hero! – Preferably Scriptman from Planet Java

  • CodySCarlson

    @ Et Al,

    IF YOU’VE BEEN STRUGGLING, LIKE ME, WITH that ol’ app.get thang… Try removing the ” * ” symbol from your Express code:

    app.get(‘/’, function(req, res){ res.sendfile(__dirname + ‘/public/index.html’); });


    I can still request:


    AND even simply

    …and everything works great!

    I have angular’s $locationProvider.html5Mode( true ); set as well.

    Hope this saves you from what I went through :)

    • Juan Biscaia

      Hi, I have one issue with the app.get(‘/’) too… when I put this thing in my server.js i’m not capable of load any local .js file inside the index.html file… I can access my app just by using the localhost:8080/, but cant load the core.js for AngularJS …

  • Pingback: Fork the Couch (Messin’ with the Couch – Part 2) « Code Pterodactyl

  • Pingback: Nodejs,express,jade,mangodb | Tech Geeks

  • Justin

    I’m new to web development and Java Script. I’m trying to figure out where the values ({todos.length}, {todo.text}) come from in the index.html. Can someone explain this to me please? Thanks.

  • Pingback: 3 steps to JS bliss | My Rants on Life and Technology

  • HHH

    Hi Chris.. I feel this is an awesome example of writing the code in a neat way. But I think it is a bit difficult to understand it for a starter. If you can elaborate more on the application structure and the connections and how we should split the code across different directory structure, it’ll be really helpful or else you can suggest me any similar readings with more basics explained..

  • Bryan Christophe Green

    This code doesn’t work as expected (the data brought back from the DB is written to the console, not the $scope, for one thing). The code in the Github repo is totally different from the code in this tut– which I didn’t realize until I spent a couple of hours debugging everything. All in all a good tutorial, though, as long as this code gets updated.

  • David Coffee Sivocha

    Just a usability thought. Instead of having ng-click on the button, you would be better off adding that as ng-submit on the form tag.

    This would allow your users to submit the form by enter as well, which means you could do away with the button etc.

    Other than that, good tutorial!

  • Chris Sevilleja

    What do you feel needs to be updated? The only thing I can see is that it should be updated for Express 4.0 which only requires about 2 lines of changes. (removing app.configure() and adding the morgan package).

    At a quick glance, upgrading Angular to the 1.2 version also doesn’t require changes. If you have any updates, I’d be happy to update the article.

  • Javier Rosas

    Hi Scotch. Can you make a tutorial of how to integrate Angular.js + Twitter Bootstrap?

  • Rajanikanth

    Thank you for Your tutorial.

  • Pingback: Mean | Pearltrees

  • Abdussami

    Till this hyperlink, I copied and pasted your code. It gave me some middleware update errors, which I removed. But the page doesn’t load. What could the problem be? It just keeps hanging. Not even a 404 error, I checked it using Fiddler.

  • Pingback: 1. To-Make List App | ShMcK

  • Phillip Walker

    Does anyone know how to resolve the {{todos.length}} problem? Before everyone claims this has been resolved, I have tried implementing the changes already suggested but maybe I’m missing something bc they arn’t working :-(

    I have grabbed the exact Github version afterpain stakingly trying to debug this. I have attached a picture. If anyone can help I’d be eternally thankful!


    • shahaf

      I also have this problem, It seems like the index.html cant load the local core.js file

      • Chris Sevilleja

        I’ve updated this article to use the updated version of Express (4.0 instead of 3.0).

        Is the line app.use(express.static(__dirname + '/public')); in your code? That’s how Node knows where to look for things like the core.js file.

  • Rubens Mariuzzo

    Thanks for your time put in writing this. Keep inspiring us!

    • Chris Sevilleja

      Thank you for the kind words. I’m working on a giant MEAN stack series that will be released soon so stay tuned for that!

  • Pingback: 【Node.js/AngularJS】でアプリケーションを開発する。