Web Development

10 Tips For Optimizing Node.js Applications

July 5th, 2016 | By Jscrambler | 4 min read

We will explore ten expert tips for optimized performance in Node.js applications.

1. Always use asynchronous functions

Why should we always write asynchronous functions?
Because this is the best part of Node.js, all asynchronous functions perform a non-blocking I/O, avoiding CPU idle.

For an application that runs a lot of I/Os, this simple trick will make the servers work more and better because a server processing non-blocking I/O can handle multiple requests while one of these requests is performing an I/O. See the example:

var fs = require('fs');
// Performing a blocking I/O
var file = fs.readFileSync('/etc/passwd');
console.log(file);
// Performing a non-blocking I/O
fs.readFile('/etc/passwd', function(err, file) {
    if (err) return err;
    console.log(file);
});


2. Use the async module for better function organization


One of the challenges of working with asynchronous functions is to handle multiple chained callbacks.

Many chained callbacks make the code ugly and difficult to read: a classic callback hell.

var fs = require('fs');
// A common callback hell example
fs.readFile('/etc/passwd', 'utf8'function(passwdErr, passwd) {
    if (passwdErr) return passwdErr;
    fs.readFile('/etc/hosts', 'utf8', function(hostsErr, hosts) {
        if (hostsErr) return hostsErr;
        fs.mkdir(__dirname + '/test', function(dirErr) {
            if (dirErr) return dirErr;
            var data = passwd + hosts;
            fs.writeFile(__dirname + '/test/data', data, 'utf8', 
                function(err) {
                console.log('Done!');
            });
        });
    });
});


To avoid this callback hell, you can use the async module. This is a great module that has a lot of tricks and magic to deal with multiple asynchronous functions. See this example that eliminates the callback hell:

// You must install async before: npm install async
var async = require('async');
var fs = require('fs');

async.waterfall([
    function(callback) {
        fs.readFile('/etc/passwd', 'utf8', callback);
    },
    function(passwd, callback) {
        fs.readFile('/etc/hosts', 'utf8', function(err, hosts) {
            if (err) {
                return callback(err);
            }
            var data = passwd + hosts;
            return callback(null, data);
        });
    },
    function(data, callback) {
        fs.mkdir(__dirname + '/test', function(err) {
            if (err) {
                return callback(err);
            }
            return callback(null, data);
        });
    },
    function(data, callback) {
        fs.writeFile(__dirname + '/test/data', data, 'utf8', callback);
    }
], function(err) {
    if (err) {
        console.log(err);
        return;
    }
    console.log('Done!');
});


At first sight, this code looks bigger than the first one, but the async.waterfall uses an array structure to deal with multiple asynchronous functions better than the traditional callback method.

Another async feature is the async.parallel(), which executes asynchronous tasks in parallel.

3. Use ES6 Generators to organize asynchronous functions

Another way to avoid the callback hell is by using Generators from ES6. See how the previous example will look like using this alternative solution:

var fs = require('fs');

function* fileTask() {
    var passwd =
        yield fs.readFile('/etc/passwd', 'utf8');
    var hosts =
        yield fs.readFile('/etc/hosts', 'utf8');
    var data = passwd + hosts;
    yield fs.mkdir(__dirname + '/test');
    yield fs.writeFile(__dirname + '/test/data', data, 'utf8');
}

var task = fileTask();

task.next(); // Runs the "yield fs.readFile('/etc/passwd', 'utf8');
task.next(); // Runs the "yield fs.readFile('/etc/hosts', 'utf8');
task.next(); // Runs the "yield fs.mkdir(__dirname + '/test');
task.next(); /* Runs the "yield fs.writeFile(__dirname +
             '/test/data', data, 'utf8');*/


4. Use Node.js to send data

Node.js servers are better when they work to send data instead of an entire HTML page. This is why this platform became so popular for REST APIs.

In general, this kind of application works with JSON data, which is native to JavaScript and since Node.js is JavaScript, there are no parsing tasks between JSON and other data formats, so there is only JSON traffic between the server and clients, which increases the server’s performance by eliminating the parsing tasks.

5. Use Nginx or Apache for static servers


Don’t waste your Node servers by letting them serve static files, because there are servers like Nginx and Apache which work better than Node.js for this task. The reason is simple, Node.js works better processing data instead of serving static files. Nginx and Apache servers have a lot of configuration for serving static files and also have useful cache strategies. Like I said before, always use Node servers for processing data.

6. Avoid cookies and sessions

Cookies and sessions are techniques to store temporary states in the server. Keeping states is an expensive cost for servers.

Today, it is very common to build stateless APIs that provide token authentications like JWT, OAuth, and others. These tokens are kept on the client-side and prevent the servers from managing states.

7. Use cluster module for parallel processing

The cluster module is native to Node.js and it creates a lot of processes of the application including a master cluster which acts as the load balancer to distribute requests among all the slave clusters.

This technique optimizes your servers by using all the CPU cores to work with parallel processing.

8. Enable Streaming responses

The stream module is native. It allows the streaming of large data for a specific response.

This is very useful when the server needs to send videos, audio, and any type of data because the stream allows the request to send pieces of data instead of all of it and this technique avoids the buffer’s overflow on the server.

9. Always use the latest stable version

This tip is too obvious, but always use the latest stable version of Node.js because of the improvements for JavaScript V8 runtime, which often comes with a better optimization for memory and CPU uses.

10. Optimize, but don’t forget to protect

Optimization is essential but not enough. You have to think about security.

At Jscrambler, we help companies optimize and protect their Node.js applications.

Sign up for our free trial, and let us know if you have any questions. We have a team of experts ready to help you!

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles

Must read next

Web Security

How To Protect Node.js Apps With Jscrambler

In this post, we're going to walk through the steps to protect your Node.js application with Jscrambler, using our integrations with Grunt and Gulp.

September 16, 2020 | By Jscrambler | 6 min read

Web Development

How to Build Real-time Applications Using Node.js and RethinkDB

The RethinkDB is awesome NoSQL! This database can provide a fully support for realtime applications just using changefeed + socket.io.

August 4, 2016 | By Jscrambler | 5 min read

Section Divider