Node.js seems to be a pretty big deal nowadays, so not long ago I decided to check it out. And I think I’ve learned enough to give you a good introductory tutorial.
In this tutorial, we’ll keep it very simple: we’ll just make a notepad app that allows users to write notes and delete them. (Have mercy on me here – I’m still getting my feet wet!)
Section 1 – Getting Node.js
Visit http://nodejs.org/ and follow the instructions relevant to your system. Also, to get warmed up, you may want to complete the example hello-world application on the Node.js homepage.
Once you’re all set, we’ll move on to section 2. (If you wrote the hello-world application, congrats!)
Section 2 – Creating the App Directory
Create a directory called myNotePad; then add a file called package.json:
myNotePad/package.json
{
"name": "myNotePad",
"description": "a simple note creator app",
"version": "0.0.1",
"private": true,
"dependencies": {}
}
This file specifies critical information about your application, and you absolutely must have one in any Node.js app you make. (Check out this page for additional details).
Now we’ll move on to, you know, actually creating the app!
Section 3 – Creating App.js
In your app’s directory, create a file called (you guessed it) app.js and add this code:
myNotePad/app.js
var http = require('http'),
path = require('path');
These are the first two modules we will need to design our app. Because they both come with Node.js, we don’t have to install them first; we just bring them right into our app. We will need the http module to set up our app’s server and the path module to use another directory that we’re going to make.
Next, we’ll install the Express module, the driving force of our app. In fact, what we’re really doing is creating an Express app with Node, and Express is amazing. 🙂
Section 4 – Installing and Requiring Express
Head back to myNotePad/package.json and add a dependency:
myNotePad/package.json
{
"name": "myNotePad",
"description": "a simple note creator app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.0.0"
}
}
Visit your terminal (cd to your app’s directory, of course) and run the following command:
npm install
Now your app has a node_modules directory, which contains a directory called express, the module you just downloaded.
What’s npm? That’s node package manager, and you got it when you got Node. Because it allows you to install Node modules seamlessly, it is your best friend.
Visit myNotePad/app.js to get the Express module ready for action:
myNotePad/app.js
var http = require('http'),
path = require('path'),
express = require(‘express’),
app = express();
Thanks to Express, we now have an app object, which we will use to make stuff happen, such as setting up our server.
Section 5 – Setting up the Server
We have another change for app.js:
myNotePad/app.js
var http = require('http'),
path = require('path'),
express = require(‘express’),
app = express(),
server = http.createServer(app);
server.listen(3000);
app.get('/', function(request, response) {
response.send(‘Hello World’);
});
Go back to your terminal and run this command:
node app.js
Then visit http://localhost:3000/and you will see hello world (my apologies if you’ve already done a version of this little exercise, but it’s good reinforcement 🙂 ). Now we know how to test drive our app!
Section 6 – Nedb
Okay, so our app is supposed to let us write notes and delete them. Well, how do we represent and save a note? Enter the nedb module, Node’s equivalent of SQLite.
Go back to myNotePad/package.json to add another dependency:
myNotePad/package.json
{
"name": "myNotePad",
"description": "a simple note creator app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.0.0",
“nedb”: "0.10.5"
}
}
Then back to the terminal and install it:
npm install
Now create a data directory in your app directory and add a file called notes.js:
myNotePad/data/notes.js
var path = require('path');
var Datastore = require('nedb');
var db = {
notes: new Datastore({ filename: path.join(__dirname, 'notes.db'), autoload: true })
};
In the db variable, you can see the Nedb module in action. With it, we are creating a datastore called notes.db; and because we set the autoload option to true, the datastore will store all our notes and load them whenever we fire up the app. (Read more about Nedb – it’s pretty cool!)
So that’s all well and good, but how are we going to make this datastore actually do stuff? Let’s create another javascript object called notes and define some methods that our app will need:
myNotePad/data/notes.js
var path = require('path');
var Datastore = require('nedb');
var db = {
notes: new Datastore({ filename: path.join(__dirname, 'notes.db'), autoload: true })
};
var notes = {
create: function(title, body, callback) {
db.notes.insert({ title: title, body: body }, callback);
},
list: function(callback) {
db.notes.find({}).exec(callback);
},
remove: function(id, callback) {
db.notes.remove({ _id: id }, callback);
}
};
module.exports = notes;
All the methods defined in the new notes object depend on the db variable, which itself contains a datastore instance called notes. Insert, find,and remove come from the datastore instance, and we’re simply borrowing them to define our methods create, list,and remove.
That module.exports line at the bottom specifies the exported object(s) of the file when it’s required in another. Go back to myNotePad/app.js and drop in the require statement for our notes.js file:
myNotePad/app.js
var http = require('http'),
path = require('path'),
express = require(‘express’),
app = express(),
server = http.createServer(app);
server.listen(3000);
app.get('/', function(request, response) {
response.send(‘Hello World’);
});
var notes = require('./data/notes');
And with that, app.js now has the notes object created in /data/notes.js.
Section 7 – Sockets and Views
Okay, let’s start using our notes datastore. Specifically, let’s allow users to add and remove notes from it.
The next module we will install is called socket.io, and it’s awesome. You can read more about it by visiting the link, but in short, it enables extremely fast communication between the client and the server – and that’s just what we want.
Once again, update package.json
myNotePad/package.json
{
"name": "myNotePad",
"description": "a simple note creator app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.0.0",
“nedb”: "0.10.5",
“socket.io”: "0.9.16"
}
}
And install socket.io by going to your terminal and running npm install again:
npm install
Now let’s use it to handle some actions:
myNotePad/app.js
var http = require('http'),
path = require('path'),
express = require(‘express’),
app = express(),
server = http.createServer(app),
io = require('socket.io').listen(server);
server.listen(3000);
app.get('/', function(request, response) {
response.send(‘Hello World’);
});
var notes = require('./data/notes');
io.sockets.on('connection', function (socket) {
notes.list(function (err, documents) {
socket.emit('list', documents);
});
socket.on('addNote', function (note) {
notes.create(note.title, note.body, function () {
io.sockets.emit('newNote', note);
});
});
socket.on('removeNote', function (note) {
notes.remove(note._id, function () {
io.sockets.emit('deletedNote', note);
});
});
});
All that io.sockets stuff is telling our server how to handle incoming actions and what outgoing actions to return. For example, the notes object’s list method when a connection is established (i.e., when a user goes to the site), and then the socket sends another list method, along with the data returned from the first call to list, back to the client. The actions addNote and removeNote, work similarly: when the server receives them, the notes object’s create and remove methods are called, and then two new methods, newNote and deletedNote, get sent back to the client.
In sum, the web sockets specify actions that the server receives and the server sends and actions that the client receives and the client sends. The overall dynamic is as follows: when the client receives an action, it handles it and sends it to the server; and when the server receives the action, it handles it and then sends another action back to the client.
Okay, but the previous socket code represents the server’s behavior, so where is the client? We will create an application controller to represent the client, and with the controller we will set up some views using Angular.js, an alternative to Ember.js.
Create a folder called public and add to it a file called angular.min.js. Next, visit https://angularjs.org/ and download the legacy, minified build. Then copy and paste the code from that build into myNotePad/public/angular.min.js.
Next, in myNotePad/public/, add a file called AppController.js:
myNotePad/public/AppController.js
var socket = io.connect();
var AppController = function($scope) {
$scope.notes = [];
$scope.addNote = function () {
var title = $scope.title,
body = $scope.body;
socket.emit('addNote', { title: title, body: body });
$scope.title = '';
$scope.body = '';
};
$scope.removeNote = function (note) {
socket.emit('removeNote', note);
};
socket.on('list', function (documents) {
$scope.$apply(function () {
$scope.notes = documents;
});
});
socket.on('newNote', function (note) {
$scope.$apply(function () {
$scope.notes.push(note);
});
});
socket.on('deletedNote', function (note) {
$scope.$apply(function () {
var i = $scope.notes.indexOf(note);
$scope.notes.splice(i, 1);
});
});
}
What’s happening here will make more sense once we have the views set up, but notice that we are defining the addNote and removeNote actions, which the user will initiate and which will go to the server. Also, we have prepared the client-side socket for the list, newNote,and deletedNote actions coming from the server.
To prepare our app’s views, let’s install two more modules: ejs and express-ejs-layouts. Go to package.json and update it accordingly:
myNotePad/package.json
{
"name": "notepad",
"description": "a simple note creator app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "4.0.0",
"ejs": "1.0.0",
"socket.io": "0.9.16",
"express-ejs-layouts": "0.3.1",
"nedb": "0.10.5"
}
}
And head back to your terminal again and run npm install.
That’s it for modules! Promise!
Now add a folder called views and add two files to it, layout.ejs and index.ejs (.ejs means an embedded-javascript file):
myNotePad/views/layout.ejs
<!DOCTYPE html>
<html ng-app>
<head>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="/angular.min.js"></script>
<script type="text/javascript" src="/AppController.js"></script>
<title>My Notes</title>
</head>
<body ng-controller="AppController">
<%- body %>
</body>
</html>
myNotePad/views/index.ejs
<h1>Welcome to NoteMaker! Woo!</h1>
<table>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
<tr>
<td colspan="2">
Title: <input type="text" ng-model="title" />
Body: <input type="text" ng-model="body" />
<button ng-click="addNote()">Add note</button>
</td>
</tr>
<tr ng-repeat="note in notes">
<td>{{note.title}}</td>
<td>{{note.body}}</td>
</tr>
</table>
<button ng-repeat="note in notes" ng-click="removeNote(note)">Remove Note</button>
Both these files prepare our views to access just about everything we’ve written so far. The layout file does all the essential script importing and prepares the app for Angular.js (and sets the layout of the app, of course); and the index file uses Angular.js to receive and display user’s notes.
Finally, let’s update our app.js file to ensure that our views get pulled in:
myNotePad/app.js
var http = require('http'),
path = require('path'),
express = require(‘express’),
app = express(),
server = http.createServer(app),
io = require('socket.io').listen(server),
expressLayouts = require('express-ejs-layouts');
var notes = require('./data/notes');
server.listen(3000);
app.set('view engine', 'ejs');
app.set('layout', 'layout.ejs');
app.use(expressLayouts);
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(request, response) {
response.render('index.ejs');
});
io.sockets.on('connection', function (socket) {
notes.list(function (err, documents) {
socket.emit('list', documents);
});
socket.on('addNote', function (note) {
notes.create(note.title, note.body, function () {
io.sockets.emit('newNote', note);
});
});
socket.on('removeNote', function (note) {
notes.remove(note._id, function () {
io.sockets.emit('deletedNote', note);
});
});
});
All right! Head to your terminal and run node app.js. You can now add and delete notes.
You have completed your first(maybe?) app using Node.js. Congratulations!
Section 8 – Doing More
Here are two more features you can implement on your own:
- Let users edit notes.
- Create a complete note show view that lets users view notes based on their ids (/:id), and make every note a link to that show view.
As always, thanks for reading, and let me know if you have any questions.
Have fun!