Notes on Node.js and Express.js - The Risberg Family
Notes on Node.js and Express.js
Created 08/04/11
Updated 12/04/11, Updated 10/24/12, Updated 05/22/13, Updated 11/21/13, Updated 03/12/14, Updated 07/19/14
Updated 09/27/14, Updated 11/08/14, Updated 12/08/14, Updated 02/21/15, Updated 04/22/15, Updated 07/25/15
Updated 08/22/15, Updated 11/15/15, Updated 02/14/16, Updated 04/27/16, Updated 06/11/16, Updated 08/11/16
Updated 11/02/16, Updated 01/18/17, Updated 02/14/17, Updated 03/14/17, Updated 06/25/17, Updated 08/25/17
Updated 09/28/17
Introduction
Node.js (also called simply Node) is a software system designed for writing highly-scalable internet applications, notably web servers. Programs are written in JavaScript, using event-driven, asynchronous I/O to minimize overhead and maximize scalability. Node.js consists of Google's V8 JavaScript engine plus several built-in libraries.
Node.js was created by Ryan Dahl starting in 2009, and its growth is sponsored by Joyent, his employer.
Similar environments written in other programming languages include Twisted for Python, Perl Object Environment for Perl, libevent for C and EventMachine for Ruby. Unlike most JavaScript, it is not executed in a web browser, but is instead a form of server-side JavaScript. Node.js implements some CommonJS specifications. Unlike most JavaScript systems, it provides a REPL environment for interactive testing.
The LTS version is 6.11.3, while the latest version is 8.6.0.
Express.js is one of the more popular application frameworks used on top of Node.js. It is currently on version 4.15.4. It adds support for routes, templates, error handling, configuration, and more.
Resources
The primary online site is at .
An article in September 2017 about use of NodeJS at large companes
There was a very good presentation from PayPal on using Node.js in enterprise applications given at CodeCamp 2013. Here is a summary:
A case study in how PayPal revitalized its tech stack by moving from Java, JSP, and proprietary solutions to node.js and dust.js templating. Developer agility was our primary motivation, but along the way we had to tackle enterprise culture and teach people that JavaScript is no longer a "toy", but a powerful tool to wield.
Link To Slides And Code
“Node.js in Action (second edition)” by Mike Cantelon, Alex Young, Marc Harter, T.J. Holowaychuk and Nathan Rajlich. Manning Press, August 2017, 362 pages. List price $49.99, Amazon price $34.86. Not yet rated on . Updated to include new material on third party libraries, development tools, etc.
“Learning Node.js: A Hands-On Guide to Building Web Applications in JavaScript (second edition)” by Marc Wandschneider. Addison-Wesley, December 2016, 320 pages. Rated 4.8 stars on .
“Node.js Web Development (Third Edition) by David Herron. Packt Publishing, June 2016, 376 pages. List price $44.99, Amazon price $40.31, used from $38.66. Rated 4.2 stars on . Comments said: this book is a great step-by-step guide to get started with NodeJS in no time. It uses a lot of detailed examples to make you understand how NodeJS works and how it can be used to implement modern requirements like using third party authentication (e.g. login with Twitter), NoSQL databases (MongoDB is used here), responsive web design, or deploying with Docker.
“Node.js in Practice” by Alex Young and Mark Harter. Manning Press, December 2014, 424 pages. List price $49.99, Amazon price $38.56, used from $35.22. Rated 5.0 stars on . Coding by example. Comments said that the book does venture into Express, outside the Node core, but in a useful, limited fashion that concentrates on the use of Node and Express to develop REST APIs. This is a very common use case for Node that needs more real world coverage and exposition of best practices.
“Web Development with Node and Express” by Ethan Brown. O’Reilly Press, July 2014, 330 pages. List price $29.99, Amazon price $24.56. Rated 5 stars on . This is a very comprehensive book about both Node and Express. We include this book under both the Node and Express sections of this document, because it takes both together, and covers the topics needed to make Node.js your complete web application server. Topics include request processing, security, persistence, deployment, etc.
“Node.js, MongoDB, and AngularJS Web Development” by Brad Dayley. Addison-Wesley Professional, June 2014, 696 pages. List price $49.99, Amazon price $32.12, Used from $28.12. Rated 3.5 stars in . Available on IEEE Safari books. Quite comprehensive – probably the most complete book of its kind. Reviews were very good, but there were some concerns about how the book was laid out.
“Node.js the Right Way: Practical, Server-Side JavaScript That Scales” by Jim R. Wilson. Pragmatic Programmers Proess, December 2013, 148 pages. Rated 4.6 stars on Amazon. List price $17.00, Amazon price $14.98, used from $13.56. A little short, but well-written.
- has a list of useful suggestions, current to late 2015.
Concepts
The core of Node.js is the ability to run JavaScript on the server. The second-most important part of Node.js is the ecosystem of conventions for organizing complex JavaScript resources, called the Node Package Manager (npm).
Within the core of Node there is:
• Event-driven programming using callbacks
• An I/O subsystem that supports files and system-level operations.
• A large number of supporting plugins
• Support from an increasingly large number of hosting/cloud services (such as Heroku)
Within NPM and its tools there is:
• A standard file for describing dependencies, called package.json
• A loader and dependency resolver (much like Maven for Java programs)
• An eco-system of registered plugins, kept track of by version.
Node.js vs. Apache: Majority of comparison on-line is really about Node.js vs. PHP on Apache. The primary difference comes down to:
Node is a long-running process, whereas Apache spawns multiple threads (one per request), which start with a fresh state every time. Every time there is a request, Apache opens a PHP thread.
When there are too many requests at the same time (i.e., under high load conditions), Apache might open too many threads and overload the CPU.
When and When not to Use it
When to choose Node.js (based on ):
• If your server side code requires very little CPU cycles. In other words you are doing non-blocking operation and does not have heavy algorithm or logic which consumes lots of CPU cycles.
• If you are from JavaScript background and comfortable in writing Single Threaded code just like client side JS.
• Node.js stands out especially when doing real time app combining with socket.io. Node.js is well suited for applications that have a lot of concurrent connections and each request only needs very few CPU cycles, because the event loop (with all the other clients) is blocked during execution of a function.
When NOT to use Node.js:
• Your server request is dependent on heavy CPU usage such as supporting a complex algorithm or logic.
• Node.js itself does not utilize all core of underlying system and it is single threaded by default, you have to write logic by your own to utilize multi-core processor and make it multi-threaded (there is now a cluster module for multi-threading).
Architecture
In Node, all requests are put in the event loop in a way similar to how JavaScript handles event listeners. Across all requests, there is only a single thread. This is in contrast to the approach in Apache Web Server and PHP.
Example of blocking vs. non blocking i/o:
// PHP
print(“Hello”);
sleep(5);
print(“World”);
// node
console.log(“Hello”);
setTimeout(function () {
console.log(“World”);
}, 5000);
The difference is not merely syntactic (Node.js uses a callback) because these examples epitomize the distinction between blocking and non-blocking code. In the first example, PHP sleep() blocks the thread of execution. While the program is sleeping, it is not doing anything else. Node registers events and then runs an infinite loop to poll the kernel to know whether these events are ready to be dispatched. When they are, the associated function callbacks are fired, and it moves on. If no events are polled, Node just keeps going until new events are ready.
How is Node.js so good at managing a great deal of network concurrency? For example, in a normal laptop, a simple HTTP server written in Node is able to handle thousands of clients per second.
The reason is that there is an event stack maintained with node, which is collected across all requests. This link explains this well:
It states:
Node.js is built upon libuv, a cross-platform library that abstracts apis/syscalls for asynchronous (non-blocking) input/output provided by the supported OSes (Unix, OS X and Windows at least).
Node tackles the problem leveraging javascript's language features to make this model a little more synchronous-looking by inducing the programmer to employ a certain programming style. Every function that requests IO has a signature like function (... parameters ..., callback) and needs to be given a callback that will be invoked when the requested operation is completed (keep in mind that most of the time is spent waiting for the OS to signal the completion - time that can be spent doing other work). Javascript's support for closures allows you to use variables you've defined in the outer (calling) function inside the body of the callback - this allows to keep state between different functions that will be invoked by the node runtime independently.
Moreover, after invoking a function spawning an IO operation the calling function will usually return
control to node's event loop. This loop will invoke the next callback or function that was scheduled for execution (most likely because the corresponding event was notified by the OS) - this allows the concurrent processing of multiple requests.
As a final remark, the phrase "everything runs in parallel except your code" does a decent job of capturing the point that node allows your code to handle requests from hundreds of thousands open socket with a single thread concurrently by multiplexing and sequencing all your js logic in a single stream of execution.
[pic]
Installation
This describes how we installed Node.js and NPM on an EC2 instance (instructions from the Coursera “Startup Engineering” course):
$ sudo apt-get update
# Install a special package
$ sudo apt-get install -y python-software-properties python g++ make
# Add a new repository for apt-get to search
$ sudo add-apt-repository ppa:chris-lea/node.js
# Update apt-get’s knowledge of which packages are where
$ sudo apt-get update
# Now install nodejs and npm
$ sudo apt-get install -y nodejs
Use npm in order to upgrade Node:
$ sudo npm cache clean -f
$ sudo npm install -g n
$ sudo n stable
Basic Example
This is from the “Smashing Node” book:
Create a server:
var http = require(“http”);
var serv = http.createServer(function (req, res) {
res.writeHead(200, { “Content-Type”: “text/html” });
res.end(“Smashing Node!”);
});
serv.listen(3000);
To run a node.js file:
$ node XXX.js
A related tool is “npm”, where npm is the name for the Node Package Manager. If you want to install something manually, you may do:
$ npm install XXX(module name)
for more details on npm, see below.
Using the Node Package Manager (npm)
This has been moved to our document “Notes on NPM and Yarn”.
Using Modules
Every file in Node.js is a module, although modules do not necessary have to be this simple.
A module typically exports something, use the “exports” construct. The exports object is a special object created by the Node module system in every file you create and is returned of the value of the function which imports that file.
Using Require
Require() is a function that is key to the module loading. Each module consists of code which is loaded into a containing module, and hence avoid polluting the namespace.
A loaded module can also export results, such as a connection object instance, which can be used by the requiring code.
The loading of modules is cached, so that a module is not loaded over and over again.
Hence, if you are writing several modules that needs submodules (and perhaps their exports), simply have each module require the submodules, almost like a Java import statement. If there are no exports then this is like a compile-time import, but since JavaScript is always executed, this is like a conditional evaluation.
A Module so Common that it should be built in
The Async module.
This enables you to create chained, waterfall sequences of executing async function calls. Use async.waterfall. The result of each function is passed to the next one.
Use async.series to carry out several functions, in which the result of each function is collected into a map.
Use async.parallel to carry out functions that are not order dependent. There is no result, but there is an error function which will be called if one of the functions fails.
Finally use async.auto which lets you mix ordered and unordered functions together into one powerful sequence of functions. The sync.auto function will future out the required order of execution, based on information in the signatures of the functions.
Using Nodemon
Just use nodemon instead of node to run your code, and now your process will automatically restart when your code changes. To install, get node.js, then from your terminal run:
npm install -g nodemon
nodemon wraps your application, so you can pass all the arguments you would normally pass to your app:
nodemon your node app
For example, if my application accepted a host and port as the arguments, I would start it as so:
nodemon ./server.js localhost 8080
Any output from this script is prefixed with nodemon, otherwise all output from your application, errors included, will be echoed out as expected.
If no script is given, nodemon will test for a package.json file and if found, will run the file associated with the main property. For instance, in ANG06, the package.json declares “main”: “web-server.js”. Hence we can just type “nodemon” to run the application.
For more information on nodemon, see the documentation.
Streams in Node
Node contains a large stream library for I/O, which is organized somewhat similarly to the stream library in Java.
You can create streams, and pipe streams to other streams and destinations.
Use `fs.createReadStream()` to pipe the given file to `process.stdout`.
`fs.createReadStream()` takes a file as an argument and returns a readable stream that you can call `.pipe()` on. Here's a readable stream that pipes its data to `process.stderr`:
var fs = require('fs');
fs.createReadStream('data.txt').pipe(process.stderr);
Here is an example of the “through” object, which takes an input stream, has a function which transforms the input, and sends the result to a stream.
var through = require('through');
rocess.stdin.pipe(through(function (buf) {
this.queue(buf.toString().toUpperCase());
})).pipe(process.stdout);
You can use the `split` module to split input by newlines. For example:
var split = require('split');
process.stdin
.pipe(split())
.pipe(through(function (line) {
console.dir(line.toString());
}));
Will buffer and split chunks on newlines before you get them. For example, for the `split.js` we just wrote we will get separate events for each line even though the data probably all arrives on the same chunk.
`concat-stream` is a write stream that you can pass a callback to, in order to get the complete contents of a stream as a single buffer.
An example of combining these concepts would be to write a HTTP web server, which can handle any size of content since it uses streams for the content:
var http = require('http');
var through = require('through');
var server = http.createServer(function (req, res) {
if (req.method === 'POST') {
req.pipe(through(function (buf) {
this.queue(buf.toString().toUpperCase());
})).pipe(res);
}
else res.end();
});
server.listen(process.argv[2]);
Websocket Streams
Websocket streams implement the websocket protocol.
var ws = require('websocket-stream');
var stream = ws('ws://localhost:8000');
stream.end('hello\n');
Promises in Node
The core idea behind promises is that a promise represents the result of an asynchronous operation. A promise is in one of three different states:
• pending - The initial state of a promise.
• fulfilled - The state of a promise representing a successful operation.
• rejected - The state of a promise representing a failed operation.
Once a promise is fulfilled or rejected, it is immutable (i.e. it can never change again).
Example
Consider the following code: we need to handle errors thrown by JSON.parse but we also need to be careful not to handle errors thrown by the callback function. By the time we've done all of this our code is a mess of error handling:
function readJSON(filename, callback){
fs.readFile(filename, 'utf8', function (err, res){
if (err) return callback(err);
try {
res = JSON.parse(res);
} catch (ex) {
return callback(ex);
}
callback(null, res);
});
}
Despite all this mess of error handling code, we are still left with the problem of the extra callback parameter hanging around. Promises help you naturally handle errors, and write cleaner code by not having callback parameters, and without modifying the underlying architecture (i.e. you can implement them in pure JavaScript and use them to wrap existing asynchronous operations).
Now we can re-write our original example as simply:
function readJSON(filename){
return readFile(filename, 'utf8').then(function (res){
return JSON.parse(res)
})
}
Since JSON.parse is just a function, we could re-write this as:
function readJSON(filename){
return readFile(filename, 'utf8').then(JSON.parse);
}
This is very close to the simple synchronous example we started out with.
Put simply, .then is to .done as .map is to .forEach. To put that another way, use .then whenever you're going to do something with the result (even if that's just waiting for it to finish) and use .done whenever you aren't planning on doing anything with the result.
jQuery
This feels like a good time to warn you that what jQuery calls a promise is in fact totally different to what everyone else calls a promise. jQuery's promises have a poorly thought out API that will likely just confuse you. Fortunately, instead of using jQuery's strange version of a promise, you can just convert it to a really simple standardized promise:
var jQueryPromise = $.ajax('/data.json');
var realPromise = Promise.resolve(jQueryPromise);
//now just use `realPromise` however you like.
Setting up a Local Dev Web Server
It's better to use a local web server instead of opening an index.html file in the browser directly, because with a web server our JavaScript apps will be able to make AJAX/XHR requests. You can tell whether it's a server or a file by looking at the URL address bar. If the address starts with file then it's a file and if it starts with http then it's a server. We'll need this feature later in the future projects. Typically, a local HTTP web server will listen to incoming requests on 127.0.0.1 or localhost.
You can get any of the open source web servers like Apache, MAMP or (my favorites because they are written in Node.js) node-static or http-server.
To install node-static or http-server, you must have Node.js and npm installed.
So assuming you have Node.js and npm on your machine, run npm i -g node-static or npm i -g http-server in your Terminal / Command Prompt. Then, navigate to the folder with the source code and
run static or http-server.
Testing
The book says to use nodeunit, Mocha, and VowsJS. We are using Mocha and NightWatch.
Mocha
Mocha is a feature-rich JavaScript test framework running on Node.js and in the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases. Hosted on GitHub.
Example:
var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal(-1, [1,2,3].indexOf(4));
});
});
});
These are tests of the functions in your code, with the outer level of Mocha organizing them, and the inner level being a set of asserts.
NightWatch
Nightwatch.js is an easy to use Node.js based End-to-End (E2E) testing solution for browser based apps and websites. It uses the powerful W3C WebDriver API to perform commands and assertions on DOM elements.
Example:
client
.url('')
.waitForElementVisible('body', 1000)
.assert.title('Google')
.assert.visible('input[type=text]')
.setValue('input[type=text]', 'rembrandt van rijn')
.waitForElementVisible('button[name=btnG]', 1000)
.click('button[name=btnG]')
.pause(1000)
.assert.containsText('ol#rso li:first-child',
'Rembrandt - Wikipedia')
.end();
In this case, the E2E testing system is spinning up an instance of your application, and looking for HTML elements on the screen.
Application Frameworks
For some languages and platforms there are frameworks that simplify the job of crafting an HTTP-based application. Perhaps the best known is Rails (for the Ruby language), but countless others include Django, Sinatra, and Cake. Most have the goal of simplifying and structuring the development of HTML applications and solving some common problems like routing the HTTP request to the correct controller code, serving static assets, and rendering HTML templates.
In the Node world several modules and frameworks also solve some of these and other problems. Some are separate modules that you can glue together, and some, like Express.js, offer an integrated solution that you can rapidly implement.
Using the Connect Framework
Node also includes some frameworks that ease the development of an HTTP-based application. One of the most popular is called Connect. Connect defines a model for middleware components and an engine to run them.
In general, middleware components are pieces that the programmer can stack together. When Connect receives an HTTP request, the middleware engine calls each of these components in order. Any of these components has access to the request object, which means that they can parse some headers and buffer and parse the request body. They can also change the response headers, write a part of the body, or end the response.
Connect comes bundled with a series of middleware components, but you can easily build your own.
Example
var connect = require('connect');
var app = connect();
// setup logger middleware
app.use(connect.logger());
// actually respond
app.use(function(req, res) {
res.end('Hello World!');
});
app.listen(8080);
Using the Express.js Framework
Express is a minimal and flexible Node.js web application framework, providing a robust set of features for building single and multi-page, and hybrid web applications.
With a myriad of HTTP utility methods and middleware support at your disposal, creating a robust user-friendly API is quick and easy.
Express provides a thin layer of features fundamental to any web application, without obscuring features that you know and love in Node.js.
To be as flexible as possible Express makes no assumptions in terms of structure. Routes and other application-specific logic may live in as many files as you wish, in any directory structure you prefer. However Express includes an application generator that sets up a standard directory structure (shown in an example below).
Available as well are third-party extensions to Express to simplify some of these patterns:
• Resourceful routing
• Namespaced routing
The current version is 4.14.0, released in mid-2016.
A prior version is 4.12.3, released in April 2015.
A prior version is 4.11.2, released early 2015. The first version of the 4.10.x series was in October 2014.
A prior version is 4.8.3, released August 10, 2014. The first version of the 4.8.x series was in late Spring 2014.
A prior version is 4.0.x. The first version of the 4.x series was in March 2014. Starting with the 4.0 versions, the Connect framework is no longer used, but similar facilities are provided within Express.
A prior version was the 3.3.x series. The final release was 3.3.5 in early 2014, and the first release was 3.0.0 in 2012.
Resources
The main site is at
“Express.js in Action” by Evan Hahn. Manning Press, April 2016, 256 pages. List price $39.99, Amazon price $36.52, used from $25.73. Rated 4.3 stars on . This book is for Express 4.x. get examples of his writing and thoughts at his web site, We learned a lot from this web site (which is for Express 4).
“Getting MEAN with Mongo, Express, Angular, and Node” by Simon Holmes. Manning Press, November 2015, 440 pages. List price $44.99, Amazon price $37.33, used from $24.00. Rated 4.7 stars on . Really comprehensive “perfect for filling in the gaps”.
“Web Development with Node and Express” by Ethan Brown. O’Reilly Press, July 2014, 330 pages. List price $29.99, Amazon price $24.56. Rated 5 stars on . This is a very comprehensive book about both Node and Express. It takes both together, and covers the topics needed to make Node.js your complete web application server, replacing Apache or JBoss, for instance.
Example
Installation
In most cases, you will use Express by specifying it in the dependencies of a project (in package.json). However, if you do want to install it at the command line, use:
$ npm install express
The quickest way to get started with Express is to utilize the executable express(1) to generate an application as shown below:
Install the executable. The executable's major version will match Express's:
$ npm install -g express-generator@4
Create the app:
$ express /tmp/foo && cd /tmp/foo
Install dependencies:
$ npm install
Start the server (typically on port 3000)
$ npm start
Example Program using Express.js
Here is the example app.js file that Express.js creates (as of 4.2.2 or later):
var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(favicon());
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
app.use('/users', users);
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
/// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
var port = Number(process.env.PORT || 3000);
app.listen(port, function() {
console.log("Listening on " + port);
});
After the initial dependency has loaded, you will find that you are instantiating an Express app using var app = express();
After this the code goes into configuring some application settings. First it sets the views setting to the root of the template files used for composing HTML pages. Then it sets the view engine setting to jade. Jade is a templating language for HTML that does what you would expect from a templating language — it allows you to render HTML blocks, render dynamic values, perform loops, and include other templates. You can take a look at some simple Jade templates that are inside the views directory that Express just created.
The last step is to call app.listen(port). Apparently the new Express generator does not add app.listen statement in the auto generated app.js file (this potential bug is described at ). So add the last four statements of the above example yourself.
Here is a typical Jade template (this creates a list of users):
h1 Users
p
a(href="/users/new") Create new profile
ul
- for(var username in users) {
li
a(href="/users/" + encodeURIComponent(username))= users[username].name
- };
Scaffolding
Express has taken a page from Ruby on Rails and provided a utility to generate scaffolding to start your Express project.
While the Express scaffolding utility is useful, it currently doesn’t generate the framework that some authors recommend using. In particular, it doesn’t provide support for the Handlebars templating language, and it also doesn’t follow some of the naming conventions commonly used.
While not all projects will use the scaffolding utility, you should take a look at it for background information.
Resulting Application Structure
The scaffold generator is simply called “express”. Here is the generated structure:
[pic]
Static Files and Views
Express relies on a middleware to handle static files and views. Middleware is a concept that will be covered in more detail below. For now, it’s sufficient to know that middleware provides modularization, making it easier to handle requests.
The static middleware allows you to designate one or more directories as containing static resources that are simply to be delivered to the client without any special handling. This is where you would put things like images, CSS files, and client-side JavaScript files.
In your project directory, create a subdirectory called public (we call it public because anything in this directory will be served to the client without question). Then, before you declare any routes, you’ll add the static middleware:
app.use(express.static(__dirname + '/public'));
The static middleware has the same effect as creating a route for each static file you want to deliver that renders a file and returns it to the client. So let’s create an img subdirectory inside public, and put our logo.png file in there.
Now we can simply reference /img/logo.png (note, we do not specify public; that directory is invisible to the client), and the static middleware will serve that file, setting the content type appropriately. Now let’s modify our layout so that our logo appears on every page:
{{{body}}}
The element was introduced in HTML5 to provide additional semantic information about content that appears at the top of the page, such as logos, title text, or navigation.
The Request and Response Objects
When you’re building a web server with Express, most of what you’ll be doing starts with a request object and ends with a response object. These two objects originate in Node and are extended by Express.
Request
Let’s look at the most useful properties and methods of the request object (all of these methods are added by Express, except for req.headers and req.url, which originate in Node):
req.params
An array containing the named route parameters.
req.param(name)
Returns the named route parameter, or GET or POST parameters. One author recommends avoiding this method (why?)
req.query
An object containing querystring parameters (sometimes called GET parameters) as name/value pairs.
req.body
An object containing POST parameters. It is so named because POST parameters are passed in the body of the REQUEST, not in the URL like querystring parameters
req.route
Information about the currently matched route. Primarily useful for route debugging.
req.cookies/req.signedCookies
Objects containing containing cookie values passed from the client. See Chapter 9.
req.headers
The request headers received from the client.
req.accepts([types])
A convenience method to determine whether the client accepts a given type or types (optional types can be a single MIME type, such as application/json, a commadelimited list, or an array). This method is of primary interest to those writing public APIs; it is assumed that browsers will always accept HTML by default.
req.ip
The IP address of the client.
req.path
The request path (without protocol, host, port, or querystring).
req.host
A convenience method that returns the hostname reported by the client. This information can be spoofed and should not be used for security purposes.
req.xhr
A convenience property that returns true if the request originated from an AJAX call.
req.protocol
The protocol used in making this request (for our purposes, it will either be http or https).
req.secure
A convenience property that returns true if the connection is secure. Equivalent to req.protocol==='https'.
Response
Lets look at the most useful properties and methods of the response object (all of these are added by Express):
res.status(code)
Sets the HTTP status code. Express defaults to 200 (OK), so you will have to use this method to return a status of 404 (Not Found) or 500 (Server Error), or any other status code you wish to use. For redirects (status codes 301, 302, 303, and 307), there is a method redirect, which is preferable.
res.set(name, value)
Sets a response header. This is not something you will normally be doing manually.
res.cookie(name, value, [options]), res.clearCookie(name, [options])
Sets or clears cookies that will be stored on the client. This requires some middleware support.
res.redirect([status], url)
Redirects the browser. The default redirect code is 302 (Found). In general, you should minimize redirection unless you are permanently moving a page, in which case you should use the code 301 (Moved Permanently).
res.send(body), res.send(status, body)
Sends a response to the client, with an optional status code. Express defaults to a content type of text/html, so if you want to change it to text/plain (for example), you’ll have to call res.set('Content-Type', 'text/plain\') before calling res.send. If body is an object or an array, the response is sent as JSON instead (with the content type being set appropriately), though if you want to send JSON, one author recommends doing so explicitly by calling res.json instead.
res.json(json), res.json(status, json)
Sends JSON to the client with an optional status code.
res.jsonp(json), res.jsonp(status, json)
Sends JSONP to the client with an optional status code.
res.type(type)
A convenience method to set the Content-Type header. Essentially equivalent to res.set('Content-Type', type), except that it will also attempt to map file extensions to an Internet media type if you provide a string without a slash in it. For example, res.type('txt') will result in a Content-Type of text/plain. There are areas where this functionality could be useful (for example, automatically serving disparate multimedia files), but in general, you should avoid it in favor of explicitly setting the correct Internet media type.
res.format(object)
This method allows you to send different content depending on the Accept request header. This is of primary use in APIs. Here’s a very simple example: res.format({'text/plain': 'hi there', 'text/html': 'hi there'}).
res.attachment([filename]), res.download(path, [filename], [callback])
Both of these methods set a response header called Content-Disposition to attachment; this will prompt the browser to download the content instead of displaying it in a browser. You may specify filename as a hint to the browser. With res.download, you can specify the file to download, whereas res.attachment just sets the header; you still have to send content to the client.
res.sendFile(path, [options], [callback])
This method will read a file specified by path and send its contents to the client. There should be little need for this method; it’s easier to use the static middleware, and put files you want available to the client in the public directory. However, if you want to have a different resource served from the same URL depending on some condition, this method could come in handy.
Selecting a Template Engine for use with Node and Express
While Jade is the default template engine supplied with Express, there are a wide range of them available. The following comparison is derived from:
|
Built into Express |Allows logic in templates |Encourages logic in templates |Reuse templates client side |Allows Bootstrap Integration |IDE Support |Partials |Notes | |Jade |yes |yes |no |No |Yes |Webstorm, sublime with add-in |Yes + Layout |Default for Express. Lots of add-ons available via npm. Documentation is incomplete but passable. Very clean syntax. Easily readable. | |EJS |yes |yes |yes |Yes |Yes |Webstorm, sublime with add-in |No |Embedded JavaScript. Default for Sails.js. Syntax is like JSP or ERB, not as clean as Mustache. | |JSHTML |yes but no |yes |no |??? |??? |??? |No |Appears to be built in but isn’t. Instead need jshtml-express. No partials support. Missing variable in template is fatal error. Website is outdated. Automated tests are failing. Difficult to find documentation. Seeks to be familar to .net developers. | |Mustache |yes (older version of hogan) |no |no |Yes |yes |Webstorm with plugin, sublime with plugin |Yes |Very popular client-side templating library, supported by many languages. Using logic-less templates can be frustrating at first | |Handlebars |no, use express-handlebars |no |no |Yes |yes |Webstorm with plugin, sublime with plugin |Yes + Layout |Template engine used by Ghost. Very popular client side library, superset of Mustache. Supports an easier to understand if syntax for those who want minimal logic. | |
Some of our thoughts and conclusions from this:
• Jade is built in to Express.js as the default, but it uses a very different HTML and template language than what we have used with Rails, PHP, or Java.
• Many of these template languages existed before Node as client-side tools. In particular Mustache is very similar to the template framework within Backbone.
• Sails is a promising web framework (see our document “Notes on Sails”), but its default template engine of EJS (probably short for Embedded JavaScript, and named to match ERB for Embedded Ruby) is not considered as easy to use.
• Mustache is named for its use of {{ and }} as template markers
• Handlebars seems to be a superset of Mustache, hence the two of them looked the same during our evals in late 2013.
We should be focusing on Handlebars, and then on Sails with Handlebars.
Changing the Template Engine to Handlebars
This is based
var express = require('express');
var path = require('path');
var expressHbs = require('express-handlebars');
var app = express();
// configure the template engine and make it the current view rendering engine
app.engine('hbs', expressHbs({extname: 'hbs', defaultLayout: 'main.hbs'}));
app.set('view engine', 'hbs');
app.get('/', function(req, res) {
res.render('index');
});
app.get('/simple', function(req,res) {
var data = {name: "Furby", color: "black", nickname: "LooLoo"};
res.render('simple', data);
});
/// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
module.exports = app;
var port = Number(process.env.PORT || 5000);
app.listen(port, function() {
console.log("Listening on " + port);
});
The most important line in this file is the one that configures the Handlebars engine. Then the res.render() methods will dispatch to this engine, find the specified template in the “views” directory, and substitute the information from the “data” hashmap into the contents. Some example template files look like:
main.hbs:
Handlebars examples with Express 4
{{{body}}}
simple.hbs:
Example: Simple
An example toy is a: {{name}}
Color: {{color}}
Nickname: {{nickname}}
Adding Helper Methods
// Configure the template engine and make it the current view rendering engine
var hbs = expressHbs.create({
extname: 'hbs',
defaultLayout: 'main.hbs',
helpers: {
formatPercentage: function (val) {
return Math.round(100 * val);
}
}
});
app.engine('hbs', hbs.engine);
app.set('view engine', 'hbs');
To use this helper in the template, code {{formatPercentage percentage}} where percentage is a float between 0 and 1.
Form Handling
If you’re using GET for your form handling, your fields will be available on the req.query object. For example, if you have an HTML input field with a name attribute of email, its value will be passed to the handler as req.query.email. There’s really not much more that needs to be said about this approach: it’s just that simple.
If you’re using POST (recommended), you’ll have to link in middleware to parse the URL-encoded body.
First, install the body-parser middleware (npm install --save body-parser), then link it in:
app.use(require('body-parser')());
Occasionally, you will see the use of express.bodyParser discouraged, and for good reason. However, this issue went away with Express 4.0, and the body-parser middleware is safe and recommended.
Once you’ve linked in body-parser, you’ll find that req.body now becomes available for you, and that’s where all of your form fields will be made available. Note that req.body doesn’t prevent you from using the querystring. Let’s go ahead and add a form that lets the user sign up for a mailing list. For demonstration’s sake, we’ll use the querystring, a hidden field, and visible fields in /views/newsletter.handlebars:
Sign up for our newsletter to receive news and specials!
Name
Email
Register
Note we are using Twitter Bootstrap styles, as we will be in other examples. If you are unfamiliar with Bootstrap, you may want to refer to our file “Notes on Twitter Bootstrap”. Here is the application.js file:
app.use(require('body-parser')());
app.get('/newsletter', function(req, res){
// we will learn about CSRF later...for now, we just provide a dummy value
res.render('newsletter', { csrf: 'CSRF token goes here' });
});
app.post('/process', function(req, res){
console.log('Form (from querystring): ' + req.query.form);
console.log('CSRF token (from hidden form field): ' + req.body._csrf);
console.log('Name (from visible form field): ' + req.body.name);
console.log('Email (from visible form field): ' + req.body.email);
res.redirect(303, '/thank-you');
});
That’s all there is to it. Note that in our handler, we’re redirecting to a “thank you” view.
We could render a view here, but if we did, the URL field in the visitor’s browser would remain /process, which could be confusing: issuing a redirect solves that problem. It’s very important that you use a 303 (or 302) redirect, not a 301 redirect in this instance. 301 redirects are “permanent” meaning your browser may cache the redirection destination.
If you use a 301 redirect and try to submit the form a second time, your browser may bypass the /process handler altogether and go directly to /thankyou since it correctly believes the redirect to be permanent. The 303 redirect, on the other hand, tells your browser “Yes, your request is valid, and you can find your response here”.
Cookies and Sessions
Cookies
Before you start setting and accessing cookies in your app, you need to include the
cookie-parser middleware. First, npm install --save cookie-parser, then:
app.use(require('cookie-parser')(credentials.cookieSecret));
Once you’ve done this, you can set a cookie or a signed cookie anywhere you have access to a request object:
res.cookie('monster', 'nom nom');
res.cookie('signed_monster', 'nom nom', { signed: true });
Signed cookies take precedence over unsigned cookies. If you name your signed cookie signed_monster, you cannot have an unsigned cookie with the same name (it will come back as undefined).
To retrieve the value of a cookie (if any) sent from the client, just access the cookie or signedCookie properties of the request object:
var monster = req.cookies.monster;
var signedMonster = req.signedCookies.monster;
You can use any string you want for a cookie name. For example, we could have used 'signed monster' instead of 'signed_monster', butthen we would have to use the bracket notation to retrieve the cookie: req.signedCookies['signed monster']. For this reason, I recommend using cookie names without special characters.
To delete a cookie, use req.clearCookie:
res.clearCookie('monster');
Sessions
First, install express-session (npm install --save express-session); then, after linking in the cookie parser, link in express-session:
app.use(require('cookie-parser')(credentials.cookieSecret));
app.use(require('express-session')());
The express-session middleware accepts a configuration object with the following options:
key
The name of the cookie that will store the unique session identifier. Defaults to connect.sid.
store
An instance of a session store. Defaults to an instance of MemoryStore, which is fine for our current purposes.
cookie
Cookie settings for the session cookie (path, domain, secure, etc.). Regular cookie defaults apply.
Using Sessions
Once you’ve set up sessions, using them couldn’t be simpler: just use properties of the request object’s session variable:
req.session.userName = 'Anonymous';
var colorScheme = req.session.colorScheme || 'dark';
Note that with sessions, we don’t have to use the request object for retrieving the value and the response object for setting the value: it’s all performed on the request object. (The response object does not have a session property.)
To delete a session, you can use JavaScript’s delete operator:
req.session.userName = null; // this sets 'userName' to null, // but doesn't remove it
delete req.session.colorScheme; // this removes 'colorScheme'
Middleware
Much of the middleware previously bundled with Express is quite fundamental, so it’s important to know “where it went” and how to get it. You will almost always want Connect, so it’s recommended that you always install it alongside Express (npm install --save connect), and have it available in your application (var connect = require(connect);).
Email Sending
First, we need to install the Nodemailer package:
npm install --save nodemailer
Then, require the nodemailer package and create a Nodemailer instance (a “transport” in Nodemailer parlance):
var nodemailer = require('nodemailer');
var mailTransport = nodemailer.createTransport('SMTP',{
service: 'Gmail',
auth: {
user: credentials.gmail.user,
pass: credentials.gmail.password,
}
});
Notice we’re using the credentials module. You’ll need to update your credentials.js file accordingly:
module.exports = {
cookieSecret: 'your cookie secret goes here',
gmail: {
user: 'your gmail username',
password: 'your gmail password',
}
};
Now that we have our mail transport instance, we can send mail. We’ll start with a very simple example that sends text mail to only one recipient:
mailTransport.sendMail({
from: '"Meadowlark Travel" ',
to: 'joecustomer@',
subject: 'Your Meadowlark Travel Tour',
text: 'Thank you for booking your trip with Meadowlark Travel. ' +
'We look forward to your visit!',
}, function(err){
if(err) console.error( 'Unable to send email: ' + error );
});
Routing
We’ve already seen very basic route: simply matching a given path. But what does app.get('/foo',…) actually do? It is simply a specialized piece of middleware, down to having a next method passed in. Let’s look at some more sophisticated examples:
app.get('/foo', function(req,res,next){
if(Math.random() < 0.5) return next();
res.send('sometimes this');
});
app.get('/foo', function(req,res){
res.send('and sometimes that');
});
In the previous example, we have two handlers for the same route. Normally, the first one would win, but in this case, the first one is going to pass approximately half the time, giving the second one a chance. We don’t even have to use app.get twice: you can use as many handlers as you want for a single app.get call. Here’s an example that has an approximately equal chance of three different responses:
app.get('/foo',
function(req,res, next){
if(Math.random() < 0.33) return next();
res.send('red');
},
function(req,res, next){
if(Math.random() < 0.5) return next();
res.send('green');
},
function(req,res){
res.send('blue');
},
)
For more information routing,
With the inclusion of the Express 4.0 Router, we are given more flexibility than ever before in defining our routes. To recap, we can:
• Use express.Router() multiple times to define groups of routes
• Apply the express.Router() to a section of our site using app.use()
• Use route middleware to process requests
• Use route middleware to validate parameters using .param()
• Use app.route() as a shortcut to the Router to define multiple requests on a route
With all the ways we can define routes, our applications will benefit going forward.
One way to think about the Router object is that it is like a mini-application: you send requests to it, and it dispatches to either other routers, or to the “get”, “post”, etc methods on its url. This is why the sub-parts of a complex application are located in a directory of “routes”.
The order in which you register Routers is important, as it controls the tree. There can be multiple actions invoked if the url’s match on several paths in the tree. There is also a Router.use() method which is invoked before the more specific get or post method, this can be useful for logging.
You can also chain routers to form a deeper tree. Here is an example:
var userRouter = express.Router();
// you need to set mergeParams: true on the router,
// if you want to access params from the parent router
var itemRouter = express.Router({mergeParams: true});
// you can nest routers by attaching them as middleware:
userRouter.use('/:userId/items', itemRouter);
Persistence Approaches
Using the file system
There are operations in Node.js for this.
Using MongoDB
While there’s a low-level driver available for MongoDB, you’ll probably want to use an “object document mapper” (ODM). The officially supported ODM for MongoDB is Mongoose.
One of the advantages of JavaScript is that its object model is extremely flexible: if you want to add a property or method to an object, you just do it, and you don’t need to worry about modifying a class. Unfortunately, that kind of free-wheeling flexibility can have a negative impact on your databases: they can become fragmented and hard to optimize. Mongoose attempts to strike a balance: it introduces schemas and models (combined, schemas and models are similar to classes in traditional object-oriented programming). The schemas are flexible but still provide some necessary structure for your database.
Using a Relational Database
Examples here would include MySQL or Postgres. There are two levels at which to discuss this
1) Using the MySQL connector plugin, which allows you to manage connections and pools, perform queries, and get results sets. There is limited support for data model classes and abstractions.
2) Using an Object Relational Mapper. This generally allows you to define the schema as a set of models, and has the ORM library take care of generating the queries and mapping the results. One of the most commonly-used examples of this is Sails.js. See our document “Notes on Sails.js”.
3) Using Sequelize, which is conceptually in between the two above examples. This is described below.
For examples of the former level, see online tutorials such as:
Most of these work with the MySQL connector plugin, which is available and documented at
Sequelize
Sequelize is a promise-based ORM for Node.js. It supports the dialects PostgreSQL, MySQL, MariaDB, SQLite, and MSSQL and features solid transaction support, relations, read replication and more.
The current version is 2.0.5 as of April 2015.
For an example, see:
Setting up a connection
Sequelize will setup a connection pool on initialization so you should ideally only ever create one instance per application.
var sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'|'mariadb'|'sqlite'|'postgres'|'mssql',
pool: {
max: 5,
min: 0,
idle: 10000
},
});
// Or you can simply use a connection uri
var sequelize = new Sequelize('postgres://user:pass@:5432/dbname');
The Sequelize constructor takes a whole slew of options that are available via the API reference.
Defining Models
Models are defined with sequelize.define('name', {attributes}, {options}).
var User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING,
field: 'first_name' // Will result in an attribute that is firstName
// when user facing but first_name in the database
},
lastName: {
type: Sequelize.STRING
}
}, {
freezeTableName: true // Model tableName will be the same as the model name
});
User.sync({force: true}).then(function () {
// Table created
return User.create({
firstName: 'John',
lastName: 'Hancock'
});
});
Note that the table will be pluralized unless freezeTableName is set to true.
The User.sync operation creates the table in the database schema. In the above example, an initial record is being created.
Operations on Models
Here is an example:
var User = sequelize.define('user', {
email: DataTypes.STRING,
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
password: DataTypes.STRING
}, {
freezeTableName: true,
instanceMethods: {
retrieveAll: function (onSuccess, onError) {
User.findAll({}, {raw: true}).success(onSuccess).error(onError);
},
retrieveById: function (user_id, onSuccess, onError) {
User.find({where: {id: user_id}}, {raw: true}).success(onSuccess).error(onError);
},
add: function (onSuccess, onError) {
var email = this.email;
var firstName = this.firstName;
var lastName = this.lastName;
var password = this.password;
User.build({ email: email, firstName: firstName, lastName: lastName, password: password })
.save().success(onSuccess).error(onError);
},
updateById: function (user_id, onSuccess, onError) {
var id = user_id;
var email = this.email;
var firstName = this.firstName;
var lastName = this.lastName;
var password = this.password;
User.update({ email: email, firstName: firstName, lastName: lastName, password: password }, {where: {id: id} }).success(onSuccess).error(onError);
},
removeById: function (user_id, onSuccess, onError) {
User.destroy({where: {id: user_id}}).success(onSuccess).error(onError);
}
}
});
Here the basic operations are User.build, User.find, User.findAll, User.save, User.update, and User.destroy.
Additional operations such as User.retrieveAll, and User.retrieveById have been defined. These provide additional error handling and value setting, for support of REST api functions.
Associations between models
One-To-Many associations are connecting one source with multiple targets. The targets however are again connected to exactly one specific source.
var User = sequelize.define('User', {/* ... */})
var Project = sequelize.define('Project', {/* ... */})
// OK. Now things get more complicated (not really visible to the user :)).
// First let's define a hasMany association
Project.hasMany(User, {as: 'Workers'})
This will add the attribute ProjectId or project_id to User. Instances of Project will get the accessors getWorkers and setWorkers. We could just leave it the way it is and let it be a one-way association.
Security
The first step in providing secure services is using HTTP Secure (HTTPS). The nature of the Internet makes it possible for a third party to intercept packets being transmitted between clients and servers. HTTPS encrypts those packets, making it extremely difficult for an attacker to get access to the information being transmitted. (One says very difficult, not impossible, because there’s no such thing as perfect security. However, HTTPS is considered sufficiently secure for banking, corporate security, and healthcare.)
Enabling HTTPS for Your Express App
Once you have your private key and certificate, using them in your app is easy. Let’s revisit how we’ve been creating our server:
http.createServer(app).listen(app.get('port'), function(){
console.log('Express started in ' + app.get('env') +
' mode on port ' + app.get('port') + '.');
});
Switching over to HTTPS is simple. You put your private key and SSL cert in a subdirectory called ssl (though it’s quite common to keep it in your project root). Then you just use the https module instead of http, and pass an options object along to the createServer method:
var https = require('https'); // usually at top of file
var options = {
key: fs.readFileSync(__dirname + '/ssl/meadowlark.pem');
cert: fs.readFileSync(__dirname + '/ssl/meadowlark.crt');
};
https.createServer(options, app).listen(app.get('port'), function(){
console.log('Express started in ' + app.get('env') +
' mode on port ' + app.get('port') + '.');
});
That’s all there is to it. Assuming you’re still running your server on port 3000, you can now connect to https:
Debugging
Both Node and your browser offer you a read-eval-print loop (REPL); this is basically just a way to write JavaScript interactively. You type in some JavaScript, press Enter, and immediately see the output. It’s a great way to play around, and is often the quickest and most intuitive way to locate an error in small bits of code. In a browser, all you have to do is pull up your JavaScript console, and you have a REPL.
In Node, all you have to do is type node without any arguments, and you enter REPL mode; you can require packages, create variables and functions, or do anything else you could normally do in your code (except create packages: there’s no meaningful way to do that in the REPL).
Console logging is also your friend. It’s a crude debugging technique, perhaps, but an easy one (both easy to understand and easy to implement). Calling console.log in Node will output the contents of an object in an easy-to-read format, so you can easily spot problems. Keep in mind that some objects are so large that logging them to the console will produce so much output that you’ll have a hard time finding any useful information. For example, try console.log(req) in one of your path handlers.
Performance Testing and Analysis
Please check the following: it’s a Git page for Node.js benchmarking.
This is similar to Siege.
Analysis
Using Node.js with Heroku
The presence of package.json file indicates to Heroku that this is a Node.js application.
We deployed a simple application consisting of only the following files:
• web.js
• package.json
• Procfile
The Procfile will read “web: node web.js” or “web: node app.js”, which ever is the main file of the application.
Securing the REST API
This is similar to our needs.
Other Topics and Questions
Node.js vs. Java
Porting a RESTful Service Written in Java - See more at:
Stuff to Watch For
Since it is server-side JavaScript, the rules are more strict, there are certain pitfalls:
1. Global Namespace Pollution
2. with is evil
3. switch is evil
#1 is something that non-JavaScript developers aren't used to. #2 and #3 were probably not that serious in the context of client side JavaScript, but with Node they are really dangerous.
Appendix A: Tutorial Applications Created
NE01
Very simple Node/Express application. Uses Jade for the views. Exactly as created by the generator. Views include layouts.jade, index.jade, error.jade. Routes include index and users.
NE02
Changed from using Jade to using HandleBars. Demonstrates various ways of using template engine. Sample data about activities, users, and Furbies. Uses Twitter Bootstrap for formatting display. This also shows serving static resources. Routing examples need further review. The error view could be improved.
NE03
This is based on
Integrates Angular and Twitter Bootstrap. We have skipped over the Mongo parts for now.
NE04
(to be repackaged) Derived from IBM voting example. Adds use of MongoDB and socket.io.
ANG05
Uses Node as the backend, but simply holds state into global lists, rather than persisting.
ANG06
Uses Node as the backend, with heavy use of Sequelize to connect to a MySQL database. Has a data model of Games, Episodes, Objectives. Also a set of screens to demonstrate Angular plugins such as ui-bootstrap and ui-select.
Example Project: Proxy Server
let http = require('http');
let fs = require('fs');
let request = require('request');
let Logger = require('./lib/logger');
let yargs = require('yargs')
.usage('Usage: $0 [options]')
.help('h')
.alias('m', 'mylog')
.describe('m', 'specify logfile, otherwise use process stdout')
.alias('l', 'loglevel')
.default('l', 'debug')
.alias('u', 'url')
.default('u', '')
.describe('u', 'url that you want to forward request to')
.example('babel-node index --mylog=/tmp/cat.log', 'start the server and dump logs to specified file')
.example('babel-node index --loglevel=alert', 'set the log level to alert);
let argv = yargs.argv; //if there is no host passed from cli, use localhost as default
let port = argv.port || 80;
let destUrl = argv.url || (argv.host === "localhost" ? "" :
"http://" + argv.host + ":" + port);
let logstream = argv.mylog ? fs.createWriteStream(argv.mylog) : process.stdout;
let logLevelSetting = argv.loglevel || 'debug';
let myLog = new Logger(logLevelSetting, logstream);
//child process
let childProcess = require('./lib/childProcess');
if (argv.exec) {
childProcess.spawn(argv);
}
//set up the origin server
http.createServer((req, res) => {
for (let header in req.headers) {
res.setHeader(header, req.headers[header]);
}
req.pipe(res);
}).listen(8000);
myLog.alert("listening to localhost:8000 \n");
//set up the proxy server
http.createServer((req, res) => {
let options = {
headers: req.headers, //forward the header to dest server
url: req.headers['x-destination-url'] || destUrl
}
//make sure path and query param are forwarded.
options.url += req.url;
//request(options) is a readable, writeable stream, then pipe stream to response
var downStream = req.pipe(request(options));
myLog.debug(">< error", error)
return error
}
}
http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json')
let parentDir = path.resolve(ROOT_DIR + req.url)
async() => {
try {
let arr = await recursiveLS(parentDir)
res.writeHead(200, {
'Content-Type': 'text/plain'
})
res.end(JSON.stringify(arr))
} catch(error) {
res.writeHead(500)
res.end(error)
}
}()
}).listen(PORT, '127.0.0.1')
console.log('Server running at :', PORT);
Uses the following dependencies (The Songbird library provides tools for Asnyc. Functions marked with the async keyword, can use await within their body):
"dependencies": {
"lodash": "^3.7.0",
"songbird": "^1.0.1",
"yargs": "^3.8.0"
}
}
................
................
In order to avoid copyright disputes, this page is only a partial summary.
To fulfill the demand for quickly locating and searching documents.
It is intelligent file search solution for home and business.
Related download
- project document concentrating solar power technology
- the exchange network sharing information for a cleaner
- social service outsourcing to csos world bank
- getting started with sigil and the ace by daisy plugin
- notes on and the risberg family
- meeting on osse 7 11 2006 environmental modeling center
- howdy installation guide
- local development and promotion of led technologies for