Node.js Best Practices and Tips by Toptal Developers

This resource contains a collection of Node.js tips and best practices provided by our Toptal Network members. As such, this page will be updated on a regular basis to include additional information and cover emerging Node.js techniques. This is a community driven project, so you are encouraged to contribute as well, and we are counting on your feedback.

Check out the Toptal resource pages for additional information on Node.js. There is a Node.js job description, common Node.js mistakes, and Node.js interview questions.

Track Possibly Dead Code with Coverage Tool

What is dead code? Simply put, it is code that is never executed.

Dead or unused code is at best a pain and at worst problematic. It takes effort to maintain, including unit tests, makes your code-base bigger and more difficult to handle, especially for new engineers your hire. Even worse, you can sometimes have dead code that is highly complex (or that introduces significant complexity to the overall system) and, if you don’t realize it’s dead, you may end up “standing on your head” to get other code to work properly with it. And all this for nothing.

Fortunately, there are dead code identification tools that exist out there that can help. The problem, though, is that they statically analyze your code and try to understand which code is used and which is not.

In our example, we will use the Istanbul package. The idea here is to run this against actual production use. The way to achieve this is fairly simple:

  1. Take one production server out of the pool.
  2. Shut down your Node.js application.
  3. Run it again using Istanbul; instead of node app.js run istanbul --handle-sigint cover app.js.
  4. Wait for your application to start and put the server back in the pool.
  5. Depending on your traffic and architecture, you can leave it like this for a few minutes, hours or days. The --handle-sigint is meant to be used with long-running processes, which is exactly the case with your web server.
  6. Take the server out of the production pool again.
  7. Stop the Istanbul process. Once again the --handle-sigint option is helpful: You just have to kill the process using the SIGINT signal kill -s SIGINT [istanbul PID].
  8. Istanbul will write the code coverage report in the coverage directory and exit.
  9. Start your Node.js application in a normal way again and put the server back in the production pool.
  10. Explore the HTML coverage report to discover what code is unused.

You might be concerned about response time overhead. From my experience, the server running Istanbul had a response time around 5% higher than without it. In any case, the code coverage server should always be monitored closely.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Take Advantage of the Built-in Profiler

Performance is very important to any system. Profiling your code will allow you to understand where time is spent in your code, so you can focus on the right pieces of code that can most benefit from performance optimization.

V8’s internal profiler comes built-in with Node.js. It’s easy-to-use and very efficient.

Let’s take a look at how to use this profiler.

Running your application with profiling enabled

It’s very easy to start using profiler; simply add the --prof option to your node command:

node --prof app.js

Now let your task run, do all the HTTP calls or whatever needs to be done to execute your code. When finished, the profiler will create a “tick file”; a big text file in your current working directory named isolate-0x???????-v8.log.

Next, we need to process this file.

Processing your tick file

To process your tick file, simply run the following command:

node --prof-process isolate-0x???????-v8.log

This command will output the profiling result of your app, which consists of several parts (discussed further in the next section).

As this result can be a bit long, it is advised to pipe it into a text file.

Reading the tick file processing result

The processed result will contain several parts, including:

[Shared libraries]
[JavaScript]
[C++]
[Summary]
[C++ entry points]
[Bottom up (heavy) profile]

Jump directly to the [Summary] part. It is short and gives an immediate overview of where to dig further. For example:

[Summary]:
   ticks  total  nonlib   name
  15221   58.6%   59.7%  JavaScript
   7630   29.4%   29.9%  C++
    966    3.7%    3.8%  GC
    496    1.9%          Shared libraries
   2638   10.2%          Unaccounted

This makes it obvious that most of the time is being spent in the JavaScript functions. C++ functions are taking quite a bit of time as well. Worth noting: Be careful with creation of massive Date objects, or overuse of the Object.keys() function, both of which can adversely affect C++ performance.

Guided by the above summary information, you can then jump to the sections where most of the time is being spent. You will find a list of functions sorted by the number of ticks spent on each, in descending order.

You now know where to focus your efforts.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

Use a Code Change Watcher to Automatically Restart Your Node App

While developing software, you want your newly written code to be applied immediately to test it. Having to stop and restart your Node process by hand is a pain and a waste of time.

Fortunately, there are tools that address this issue. Let’s examine the three of the most popular such tools. All three offer very similar options and capabilities, so your choice will probably be more a matter of personal preference.

nodemon

nodemon is a tool to restart your Node server on file change automatically. You can configure it through the command line option of a config file. It supports listening for (or ignoring) specific directories and file extensions, delaying the restart of the process, and different handling of a clean process exit and app crashes.

You run it simply be replacing “node” with “nodemon” on the command line, which makes it really straightforward to use.

Installation: npm install -g nodemon

forever

forever provides many options for fine-tuning its behavior, like setting the working directory, appending logs to a file instead of stdout, or saving the process ID (PID) into a .pid file.

It has a different usage than nodemon that looks more like Unix services management, for example forever start app.js. It also has more extended options like coloring output or exit signal customization.

Installation: npm install -g forever

node-supervisor

node-supervisor (also known simply as supervisor) has the same features as nodemon and forever and several options to fine tune its behavior like not restarting on an error, for example. Very simple and efficient.

Installation: npm install -g supervisor

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Properly Use Logging in Node.js

When working on a new project, it is common to log debug messages using the console.log. It is native in JavaScript and prints objects in a JSON-like style.

However, there are a few issues using console.log to debug your application:

  • Once you have built your app, you must remove them all, unless you want to pollute your log files in production.
  • You have no options for filtering them.

Enter debug, a small Node.js debugging utility modeled after the Node core’s debugging technique. With debug, you can keep your debug logs in the code and use them only when you need them.

Here is how it works:

  1. Import the debug module and reference it with a variable: var debug = require('debug')('myWonderfulApp');. By doing this, you immediately have an instance set up for your application (‘myWonderfulApp’).
  2. Use the reference to log messages: debug("These are variables content", someVar, someOtherVar);.
  3. Turn on your own application’s debug messages while running your Node.js application: DEBUG=myWonderfullApp node app.js. As you can see, the DEBUG environment variable is set to the exact name we’ve used to initialize the debug module.

An additional very useful feature is that you can declare the debug variable using a namespace like this: var debug = require("debug")("myWonderfulApp:startup");.

This enables you to distinguish each type or level of debugging in your project very precisely. Then, when you run your application, you can decide to display specific subsets of your debug messages (e.g., DEBUG=myWonderfullApp:startup node app.js) or all debug messages (e.g., DEBUG=myWonderfullApp:* node app.js).

Finally, if you use frameworks like ExpressJS, you can activate many logs by starting your app like this: DEBUG=* node app.js. This will display all debug messages from all namespaces, including yours.

Contributors

Like what you're reading?
Get the latest updates first.
No spam. Just great engineering posts.
Like what you're reading?
Get the latest updates first.
Thank you for subscribing!
You can edit your subscription preferences here.

How to Properly Use Promises

Promises are wonderful - they enable developers to avoid cascading callbacks and make the code way clearer.

However, when you work with promises, you should avoid as much as possible creating new promises for each new request and managing them yourself.

Let’s examine the following code:

var makeRequest = function(options){
    var deferred = Q.defer();
    request.call(options, function(err, result){
        if(err){
            return deferred.reject(err);
        }
        deferred.resolve(result);
    });
    return deferred.promise;
};

var getRequest = function(url){
    var deferred = Q.defer();
    var options = {
        method: "GET",
        url: url
    };
    makeRequest(options)
        .then(function(result){
            deferred.resolve(result);
        })
        .catch(function(error){
            deferred.reject(error);
        })
    ;
    return deferred.promise;
};

var getMyAccount = function(accountId){
    var deferred = Q.defer();
    getRequest('/account/'+accountId)
        .then(function(result){
            deferred.resolve(result);
        })
        .catch(function(error){
            deferred.reject(error);
        })
    ;
    return deferred.promise;
};

The issue here is that a new promise is created in each function, but it is resolved depending on another sub-promise resolution. This development practice makes unnecessary code, more difficult to read and less efficient. We should refactor it into the following, more promise-friendly, version:

var makeRequest = function(options){
    var deferred = Q.defer();
    request.call(options, function(err, result){
        if(err){
            return deferred.reject(err);
        }
        deferred.resolve(result);
    });
    return deferred.promise;
};

var getRequest = function(url){
    var options = {
        method: "GET",
        url: url
    };
    return makeRequest(options);
};

var getMyAccount = function(accountId){
    return getRequest('/account/'+accountId);
};

The second version avoids creating a new promise at each level, thus reducing the overhead, the possibility to make bugs or to handle errors, and is much more readable. And it works the same.

Contributors

Submit a tip

Fields marked with an asterisk (*) are required
Thanks for submitting your tip proposal
Our editorial staff will review it shortly. Please note that tips proposals are subject to review and editing, and may or may not be selected for posting, at the sole discretion of Toptal, LLC.