Authors: Rachael Sim, Ang Ze Yu
Reviewers: Shradheya Thakre, Nicholas Chua, Sam Yong, Daryl Tan, Iskandar Zulkarnaien, Tan Yuan Hong
Node is a JavaScript runtime built on Chrome’s V8 JavaScript engine. It uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. --https://nodejs.org
Node is mostly used in back-end and server side scenarios. For example, LinkedIn mobile app backend is built on Node and Uber built its massive matching system between customers and drivers on Node. However, Node can also be used in the front-end to automate tasks such as building, testing, pre and post processing code.
Now that we know what Node is, let us look at some benefits it has to offer.
To install Node, simply download the installer from the official Node website based on your OS and run it. This installs both Node and npm. Npm is a tool which will help you to search, install and manage node packages, which will be further explored later.
The following example demonstrates how a Node application imports required modules, creates a server to listen for a client's request, and then sends back a 'Hello World' response.
A Simple Hello World Server from codeburst
Create a file server.js
with following content:
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer(function(req, res) {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, hostname, function() {
console.log('Server running at http://' + hostname + ':' + port + '/');
});
After you save the file, you can execute it from your terminal:
$ node server.js
Server running at http://127.0.0.1:3000/
To test the server, open a browser tab and navigate to http://localhost:3000/. You should see 'Hello world'.
Read on to find out about how to incorporate external dependencies, manage them and use Node's module system to organize your code.
Node is designed to be event-driven. When an e.g. when a I/O operation is complete, or a timer firesevent occurs (Operation Complete above), the event handler previously registered with the event is enqueued to be run by the where JavaScript code is executedevent loop (Trigger Callback), which is single-threaded.
This means that we can avoid thread overheads and synchronization problems such as a situation where some threads are blocked due to needing access to the same locksdeadlocks and a situation where multiple operations by different threads are performed on one resource in indeterministic order, potentially causing unexpected changes to the resourcerace conditions.
I/O requests made in other languages such as Python or Java are typically blocking, which means the program remains idle (in the same thread) until the I/O operation completes.
In contrast, node allows for a non-blocking I/O model with its event-driven structure.
I/O requests are delegated to other systems (e.g. file system and databases). While an I/O operation is incomplete, the event loop can still process subsequent requests. When the I/O request is complete, the handler registered with the request is then scheduled to be executed on the event loop.
Using Node for back-end development makes it possible to share common code functionality between the front-end and back-end, which leads to less code maintainence. Since the same code base is shared, it may hence also lead to a more multidisciplinary team that is familiar with both front and back-end development, reducing the potential communication overhead involved.
Node Package Manager (npm) is used to
Anyone who wants to use your project would only need to run the following command in the command shell to load the project dependencies.
$ npm install
This command will locate the package.json
file (a file that contains all metadata information about a Node application) in the root directory and install all the dependencies specified in it.
A basic package.json
has the following structure.
{
"name": "folder_name",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": ["promise", "lock"],
"author": "",
"license": "ISC"
}
The name and a version forms a unique identifier for the package. When a package is updated, the version number must be updated. A good description string and keywords helps others discover your package.
The dependencies
property specifies dependencies needed in production while the devDependencies
property specifies dependencies needed in development.
{
"dependencies": {
"express": "~3.0.1",
"sequelize":"latest",
"bluebird": "^3.4.7",
"angular":"latest",
},
"devDependencies": {
"eslint": "^4.16.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0"
}
}
The dependencies
property maps to an object that has the name and
Version Range
It is common to find carets (^
) and tildes (~
) in the version range.
This is part of npm's powerful version specification system, which allows
users and developers of the package to keep its dependencies compatible.
version range
for each dependency. It is important to specify an appropriate version range to ensure consistency and so that users and developers will have compatible dependencies. For instance, using the latest version of a dependency may introduce breaking changes from API deprecation, making debugging harder.
It is also possible and easier to install a new dependency and update package.json
directly from the command line with
$ npm install <package_name>
Once a dependency is installed, the package's code will be added to the local /node_modules
folder.
The module section describes how to import packages in your code.
scripts
package.json
also contains a scripts
property, which allows specifying various common commands to be run.
For example, inside your package.json
, you might have
{
"scripts": {
"build": "node app.js",
"lint": "eslint **/*.js",
"lint-fix": "eslint --fix **/*.js"
}
}
Running npm run build
in the command shell will execute node app.js
and similarly npm run lint-fix
will fix your linting errors in your JavaScript files.
Apart from acting as See here for some common use cases of npm scripts!shortcuts to commonly used commands, this also sets up and encourages a consistent development and build workflow in the project.
Node's module system allows you to include other JavaScript files and thus makes it easy to reuse external libraries and organize your code into separate parts with limited responsibilities.
Node comes bundled with useful core modules such as the fs
(file system) module which includes classes, methods and events to deal with file I/O operations and the https
module which helps Node to transfer data over HTTP.
There are also many useful and well-tested modules maintained by the community and external developers such as A library providing various useful wrapper functions over JavaScript's promisesBluebird on the largest ecosystem of open source libraries in the world!npm.
This makes development a breeze -- if a specific functionality has a large development overhead, you could look to such modules to speed things up.
Importing modules is easy - simply use the require()
function and provide the module identifier or the file path.
const https = require('https'); // import a core module
const Promise = require('bluebird'); // import a non-core module
Node will first check if the module identifier passed to require
is a core module or a relative path. If so, it will return the core module or the value of module.exports
in the specified file path's code. Otherwise, Node will attempt to load the module from the node_modules
folder in the parent directory of the current module.
With Node's module system, you can create separate modules in your codebase such that each is focused on a single functionality. This makes your code more maintainable and testable.
For example, in a parser module, you could export the Parser
constructor in parser.js
like so.
function Parser(options) {
this._options = options || {};
}
Parser.prototype.parse = function (content) {
...
}
module.exports = Parser; // override the exports object
Elsewhere, the Parser constructor would then be imported as such, allowing Parser objects to be created in other modules.
const parser = require('../parser') // import content from parser.js based on relative file path. The js extension is assumed and can be excluded.
const content = 'some content';
const newParser = new Parser();
newParser.parse(content);
Node is good for:
However, Node is not suitable for
module.exports
- Tendai Mutunhire's article.package.json
- npm official documentation on package.json.