Building Web Apps using Node.js
@DaveChubbuck https://github.com/davidchubbs
What is Node.js?# file called: useless.jsconsole.log(‘Hello world’);
# if useless.js was run in the browser:
# if useless.js was run on Node.js, using Terminal:
# file called: useless.jsconsole.log(‘Hello world’);
# if useless.js was run in the browser:
# if useless.js was run on Node.js, using Terminal:
What is Node.js?
# file called: useless.jsconsole.log(‘Hello world’);
# if useless.js was run in the browser:
# if useless.js was run on Node.js, using Terminal:
What is Node.js?
• Node.js includes some utilities to make it consistent with the browser, like:
• Timer API: setTimeout
• Console API: console.log
Node.js is a Platform (not a framework)
• Node.js also includes utilities for various types of network and file I/O. These include (among others):
• http, https
• Datagram (UDP)
• NET (TCP)
• fs (File System)
Node.js is a Platform (not a framework)
Frameworks are interested in organizing your code and/or providing sugar for faster development.
Node.js provides minimal, low-level APIs necessary to do things like build a web-server, command line tool, etc. Consequentially, we consider it to be a
platform, not a framework, though there are frameworks built on top of Node.js
Complete API: http://nodejs.org/api/
Scope, Exporting, & Importing
Including logic from another file does not affect the global scope. Each file has its own scope, keeping everything in the file private unless it is exported.
module.exports & exports
# exports-example.js
var private = ‘this is private since it is not exported’;exports.public = ‘this is public’;exports.method = function () {
return private;};
# module-exports-example.js
function Constructor () {}module.exports = Constructor;
use module.exports when exporting 1 value, exports when exporting 2+ values
caveats
Do not ever reassign exports (that’s what module.exports is for). exports = {}; // DO NOT DO THIS!
Do not use both exports & module.exports in the same module, else exports will be ignored. module.exports = ‘this will work’; exports.name = ‘this will be ignored’;
require
// .js & .json can be omitted// filepaths can be relative or absolute (using __dirname)// module name can be a directory if the directory contains index.js or // ... if package.json `main` property points to alternative file// 3rd party modules can be just their name (explained later)
var obj = require(__dirname + ’./exports-example’);var Constructor = require(‘./module-exports-example’);var thirdParty = require(‘not-yours’);
obj.public //=> ‘this is public’typeof obj.method //=> ‘function’
typeof Constructor //=> ‘function’var instance = new Constructor();
To import/include a module, we use require()
# index.jsvar util = require(‘./util’),
example = require(‘./example’);console.log(util());
# example.jsvar util = require(‘./util’);util();
# util.jsvar counter = 0;module.exports = function () {
return ++counter;}
>> node index.js # what will this output?
# index.jsvar util = require(‘./util’),
example = require(‘./example’);console.log(util());
# example.jsvar util = require(‘./util’);util();
# util.jsvar counter = 0;module.exports = function () {
return ++counter;}
>> node index.js2
Modules is one of the best things about Node.js
Node’s implementation of modules is done so well that Node.js projects tend to be super modular,
promoting low coupling & high cohesion
(it also influenced Browserify, which lets us write front-end JS files using this same modular pattern)
{ "name": "package-name", "version": "1.0.0", "description": "", "author": { "name": "David Chubbs", "url": "https://github.com/davidchubbs" }, "private": true, "main": "./server.js", "scripts": { "test": "mocha", "start": "node server.js" }, "dependencies": { "express": "^4.0.0" }, "devDependencies": { "jshint": "~2.5", "mocha": "~1.21", "should": "~4.0" }, "engines": { "node": ">=0.10.0" }}
package.json
# install dependencies - will show up in ./node_modules/npm install # install packages in package.json filenpm install --production # do not install devDependencies
npm install name # install name package# to save that dependency in your package.json file, use:npm install name --save-dev # saves to devDependenciesnpm install name --save # saves to dependencies
npm rm name # remove packagenpm rm name --save # remove from package.json also
npm outdated # check if dependencies are outdatednpm outdated name # check if name is outdated
npm run name # execute scripts.name in package.jsonnpm start # shortcut for `npm run start`npm test # shortcut for `npm run test`
npm rm --help # get help on a commandnpm install name -g # run command globally
Middleware
var express = require('express');var app = express();
app.use(function (req, res, next) { res.set('X-Header', 'From Express :)'); next();});
app.use(function (req, res) { res.send('this is where you will stop');});
app.use(function (req, res, next) { res.send('you will never see this');});
app.listen(3000);
Middleware is a design pattern akin to a pipeline. Requests start at the top and flow until a particular middleware fails to use next()
>> curl -i http://localhost:3000HTTP/1.1 200 OKX-Header: From Express :)Content-Type: text/html; charset=utf-8Content-Length: 27Connection: keep-alive
this is where you will stop
Middleware
var express = require('express');var app = express();
app.use(function (req, res, next) { res.set('X-Header', 'From Express :)'); next();});
app.use(function (req, res) { res.send('this is where you will stop');});
app.use(function (req, res, next) { res.send('you will never see this');});
app.listen(3000);
Middleware is a design pattern akin to a pipeline. Requests start at the top and flow until a particular middleware fails to use next()
Mounting
...
app.use('/users', function (req, res, next) { res.set('X-Role', 'User'); next();});
app.use('/admins', function (req, res, next) { res.set('X-Role', 'Admin'); next();});
app.use(function (req, res, next) { res.send('this is where you will stop');});
...
Middleware can be mounted to base directories.
Mounting
...
app.use('/users', function (req, res, next) { res.set('X-Role', 'User'); next();});
app.use('/admins', function (req, res, next) { res.set('X-Role', 'Admin'); next();});
app.use(function (req, res, next) { res.send('this is where you will stop');});
...
Middleware can be mounted to base directories.
>> curl -i http://localhost:3000/admins/indexHTTP/1.1 200 OKX-Role: AdminContent-Type: text/html; charset=utf-8Content-Length: 27Connection: keep-alive
this is where you will stop
Mounting
...
app.use('/users', function (req, res, next) { res.set('X-Role', 'User'); next();});
app.use('/admins', function (req, res, next) { res.set('X-Role', 'Admin'); next();});
app.use(function (req, res, next) { res.send('this is where you will stop');});
...
Middleware can be mounted to base directories.
>> curl -i http://localhost:3000/admins/indexHTTP/1.1 200 OKX-Role: AdminContent-Type: text/html; charset=utf-8Content-Length: 27Connection: keep-alive
this is where you will stop
>> curl -i http://localhost:3000/usersHTTP/1.1 200 OKX-Role: UserContent-Type: text/html; charset=utf-8Content-Length: 27Connection: keep-alive
this is where you will stop
Mounting
...
app.use('/admins', function (req, res) { res.send(req.url);});
...
The base directory is stripped from the mounted middleware function, meaning the mounted middleware does not have to understand where it is mounted.
Mounting
...
app.use('/admins', function (req, res) { res.send(req.url);});
...
The base directory is stripped from the mounted middleware function, meaning the mounted middleware does not have to understand where it is mounted.
>> curl -i http://localhost:3000/admins/indexHTTP/1.1 200 OKContent-Type: text/html; charset=utf-8Content-Length: 27Connection: keep-alive
/index
app.get('/profile', function (req, res) { res.send('view profiles');});
app.post('/profile', function (req, res) { res.send('update profiles');});
app.get('/profile', function (req, res) { res.send('view profiles');});
app.post('/profile', function (req, res) { res.send('update profiles');});
app.get('/profile/:id', function (req, res) { res.send('profile w/ id: ' + req.params.id);});
app.get('/profile', function (req, res) { res.send('view profiles');});
app.post('/profile', function (req, res) { res.send('update profiles');});
app.get('/profile/:id', function (req, res) { res.send('profile w/ id: ' + req.params.id);});
app.param('name', function (req, res, next, name) { // load user before hand using `name` - add to req.user next();});
app.get('/user/:name', function (req, res) { // use req.user});
app.all(‘/any-http-method’, function (req, res) { // no matter the http-method (GET, POST, PUT, etc.) // this will catch all requests @ /any-http-method ...});
app.route(‘/write/path/once’) .all(function (req, res, next) { // do something BEFORE method-specific handling next(); .get(function (req, res) { ... }) .post(function (req, res) { ... });
// Routers are isolated, making them akin to mini-appsvar router = express.Router(); // create a router
// add custom middleware to routerrouter.use(function (req, res, next) { next(); // use like any other middleware});
// add routesrouter.route(‘/profile’) .get(function (req, res) { ... });
app.use(‘/users’, router);
// catching 404’s involves having a route like this after your routesapp.use(function (req, res) { res.status(404).send(‘Page not found’);});
// then catch 500’s// (all 4 arguments are required)app.use(function (err, req, res, next) { console.error(err); res.status(500).send(‘Server error occurred’);});
// if an error occurs, you can immediately pass control to// your 500/error route by returning next(err)
app.param(‘id’, function (req, res, next, id) {
User.find(id, function (err, user) { if (err) { return next(new Error(‘User could not be found’)); }
req.user = user; next(); });
});
// setup templating// ================
var cons = require(‘consolidate’);
// set .ejs files will use EJS templating engineapp.engine(‘ejs’, cons.ejs);
// set .ejs as the default extensionapp.set(‘view engine’, ‘ejs’);// set templates pathapp.set(‘views’, __dirname + ‘/views’);
// render template// ===============
app.get(‘/path’, function (req, res) {
// renders template: __dirname + ’/views/template-file.ejs’ res.render(‘template-file’, { dynamicData: 1 });
});
var compress = require('compression');var bodyParser = require('body-parser');var logger = require('morgan');
app.use(compress());app.use(bodyParser());app.use(express.static(__dirname + '/public'));app.use(logger('dev'));// or write custom syntax - :method :url :status :res[content-length]
var cookie = require('cookie-parser');var session = require('express-session');var RedisSession = require("connect-redis")(session);
// option 1: basic cookiesapp.use(cookie());// in middleware, use res.cookie('name', 'value') to set, req.cookies.name to get
// option 2: signed cookiesapp.use(cookie("secret-used-to-sign-cookies"));// res.cookie('name', 'value', {signed: true}), req.signedCookies.name
// option 3: sessionsapp.use(cookie());app.use(session({ store : new RedisSession({ port:6379, host:'localhost' }), name : 'sessions', secret: 'secrect-session-hash', cookie: { maxAge : 10800 * 1000 } // seconds * milliseconds}));
// option 4: see example using passport for auth
req.params //=> {object} route paramatersreq.query //=> {object} parsed query-string ?n=value => {n:value}req.body //=> {object} parsed form input
req.route //=> {object} matched route details
req.get(header) //=> {string} get header field (case insensitive)req.ipreq.pathreq.url //=> {string} .path + .queryreq.baseUrl //=> {string} prefix when mountedreq.originalUrl //=> {string} .baseUrl + .url (untouched)req.secure //=> {boolean} if SSL is usedreq.hostname //=> {string} header Host valuereq.subdomains //=> {array}
res.set(headerName, value)res.status(404) // set status code of responseres.cookie(name, value, [options])res.clearCookie(name, [options])
// generating a response packet// ============================res.send(data)res.render(template, [data, callback])res.json(data)res.redirect([status=302], url)// url can be host relative (‘/path’) or path relative (‘add-to-path’)
Go to http://expressjs.com/4x/api.html for the full API docs.
This will flesh out the remaining `req` & `res` object properties.
Top Related