Node.js Notes

Introduction

Node is a runtime environment for executing JS code. Essentially, Node is a C++ program that embeds Chrome’s v8 engine. Node applications are single-threaded. That means a single thread is used to serve all clients. Node applications are asynchronous or non-blocking by default.

Scope

We don’t have the window object in Node. The global object in Node is “global”. Unlike browser applications, variables we define are not added to the “global” object.

Every file in a Node application is a module. Node automatically wraps the code in each file with an IIFE (Immediately-invoked Function Expression) to create scope. So, variables and functions defined in one file are only scoped to that file and not visible to other files unless explicitly exported. To export a variable or function from a module, you need to add them to module.exports:

module.exports.sayHello = sayHello;

To load a module, use the require function. This function returns the module.exports object exported from the target module:

const logger = require(‘./logger’);

EventEmitter is one of the core classes in Node that allows us to raise (emit) and handle events. Several built-in classes in Node derive from EventEmitter. To create a class with the ability to raise events, we should extend EventEmitter:

class Logger extends EventEmitter {
}

NPM

Every Node application has a package.json file that includes metadata about the application. This includes the name of the application, its version, dependencies, etc. All the installed packages and their dependencies are stored under node_modules folders. This folder should be excluded from the source control. Node packages follow semantic versioning: major.minor.patch. Useful NPM commands are:

# Initialize
npm init --yes
# Install a package
npm i <packageName>
# Install a specific version of a package
npm i <packageName>@<version>
# Install a package as a development dependency
npm i <packageName> —save-dev
# Uninstall a package
npm un <packageName>
# List installed packages
npm list —depth=0
# View outdated packages
npm outdated
# Update packages
npm update

jshint is a module to help check if a js file has error

$ jshint app.js

Building RESTful APIs with Express

REST defines a set of conventions for creating HTTP services: - POST: to create a resource - PUT: to update it - GET: to read it - DELETE: to delete it

Express is a simple, minimalistic and lightweight framework for building web servers.

// Build a web server
const express = require(‘express’);
const app = express();

// Creating a course
app.post(‘/api/courses’, (req, res) => {
    // Create the course and return the course object
    res.send(course);
});

// Getting all the courses
app.get(‘/api/courses’, (req, res) => {
    // To read query string parameters (?sortBy=name)
    const sortBy = req.query.sortBy;
    // Return the courses
    res.send(courses);
});

// Getting a single course
app.get(‘/api/courses/:id’, (req, res) => {
    const courseId = req.params.id;
    
    // Lookup the course
    // If not found, return 404
    res.status(404).send(‘Course not found.’);

    // Else, return the course object
    res.send(course);
});

// Updating a course
app.put(‘/api/courses/:id’, (req, res) => {
    // If course not found, return 404, otherwise update it
    // and return the updated object.
});

// Deleting a course
app.delete(‘/api/courses/:id’, (req, res) => {
    // If course not found, return 404, otherwise delete it
    // and return the deleted object.
});

// Listen on port 3000
app.listen(3000, () => console.log(‘Listening…’));

We use Nodemon to watch for changes in files and automatically restart the node process.

$ nodemon index.js

We can use environment variables to store various settings for an application. To read an environment variable, we use process.env.

const port = process.env.PORT || 3000;
app.listen(port);

You should never trust data sent by the client. Always validate! Use Joi package to perform input validation. Use postman plugin in chrome to send HTTP requests to test the server.

Express - advanced topics

Middleware

A middleware function is a function that takes a request object and either terminates the request/response cycle or passes control to another middleware function.

Express has a few built-in middleware functions: - json(): to parse the body of requests with a JSON payload - urlencoded(): to parse the body of requests with URL-encoded payload (this is old fashioned way) - static(): to serve static files, e.g. css, html, txt in the public folder

You can create custom middleware for cross-cutting concerns, such as logging, authentication, etc.

// Custom middleware (applied on all routes)
app.use(function(req, res, next) {
    // …
    next();
});

// Custom middleware (applied on routes starting with /api/admin)
app.use(‘/api/admin’, function(req, res, next) {
    // …
    next();
});

Other 3rd party middleware can be found on expressjs.com. E.g. morgan for logging, and helmet for security.

Configuration

We can detect the environment in which our Node application is running (development, production, etc) using process.env.NODE_ENV and app.get(‘env’).

console.log('NODE_ENV: ${process.env.NODE_ENV}');   // return underined if not set with e.g. $ export NODE_ENV=production
console.log('app: ${app.get('env')}');              // returns development if not set

For 3rd party configuration management package, config is prefered over rc. Create config folder and create default.json, development.json, production.json, custom-environment-variables.json inside. (custom-environment-variables.json maps environment variables and adds that to the other configs.)

const config = require('config');
console.log('Application name: ' + config.get('name'));

Debug logging with debug package

const startupDebugger = require('debug')('app:startup');
const dbDebugger = require('debug')('app:db');

startupDebugger('Starting up...');

Two ways to define environment variables in bash

$ export DEBUG=app:startup,app:db
$ nodemon index.js

or

$ DEBUG=app:* nodemon index.js

Template engine for dynamic html view rendering

To return HTML markup to the client, use a templating engine. There are various templating engines available out there. Pug, EJS and Mustache are the most popular ones.

Using pug as example.

app.set('view engine', 'pug');  // no need for require
app.set('views', './views');    // views is default path where the view is stored even if we didn't set it here

in views folder create index.pug, pug has a clearer syntax than html. Then in index.js:

app.get('/', (req, res) => {
    res.render('index', { title: 'My Express App', message: 'Hello'});
});

Restructuring using express.router

In router/courses.js

const express = require('express');
const router = express.Router();

router.get('/', (req, res) => {...});

module.exports = router;

in index.js

const courses = require('./router/courses');

app.use('/api/courses', courses);