JavaScript Best Practices- Node.js Apps
By: John Au-Yeung
Tags: Technology, JavaScript, Software Development, Programming, Web Development

Photo by David Clode on Unsplash

JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.

In this article, we’ll look at some mistakes that are easy to make in Node apps, including joining paths the wrong way, referencing process.env and more.

No String Concatenation with __dirname and __filename

In Node.js, __dirname and __filename are strings that have the current directory or current script file name respectively.

Many people try to concatenate them like the following to get the full path of a file:

const pathname = __dirname + '/bar.js'

However, the code above is probably since Node apps can be run on different platforms, which have different styles for paths.

For instance, Windows uses backslash a path segment separation, while Linux and macOS use forward slash as separators.

Therefore, concatenation isn’t a reliable way to join paths together. It’s very easy to create an invalid path by concatenation segments together.

Instead, we should use the path.join method to combine segments together as follows:

const path = require('path');

const pathname = path.join(__dirname, 'bar.js');

Then we won’t have to worry about the path structure of the path causing issues when running in different operating systems.

The path.resolve will get the fully qualified path with the full path separators. For instance, we can write:

const path = require('path');

const pathname = path.resolve(__dirname, 'bar.js');

and we get the full path generated by Node.js.

No Reference to process.env

The process.env object is used to store deployment or configuration settings. Putting it everywhere is a maintenance issue because we’re referencing this global dependency everywhere.

It can, therefore, lead to merge conflicts and deployment issues in multiserver setups.

The best practice for storing configuration settings is to store them all in one file and then reference that file instead.

For instance, instead of writing:

const dbPath = process.env.DB_PATH;

We should write:

const config = require("./config");

const dbPath = config.DB_PATH;

No process.exit()

process.exit() is use to immediately stop a Node process and exit the program.

This isn’t good because there may be lots of things running in the background that are needed by the program.

It can stop a progran complete when an error occurs.

For instance, if we have something like the following:

if (error) {

process.exit(1);

}

Then the program exists immediatelt and there’s no chance for our program to respond to the error is error is truthy.

The better way to raise errors when they occur is to throw an exception. For instance, we can do that as follows:

if (error) {

throw new Error("error");

}

This way, our program can catch the error and then respond to it. We’ll also see the full stack from the Error object from what’s been called before the error is thrown.

Photo by Emily Campbell on Unsplash

Don’t Use Synchronous Methods in Big Apps

Since Node is single-threade, running synchronous methods means that they have to finish before anything else can run. If it’s a long running process, then this is definitely a problem.

We don’t want one method to hold up our entire app. For instance, if we’re running a web server app, then we definitely shouldn’t have synchronous methods holding up the web server with one request.

However, for scripts that run line-by-line and don’t have multiple parts, synchronous methods are useful for simplifying the code and doing what we want sequentially without much thinking.

For instance, in a big production app, we shouldn’t be running methods like fs.readFileSync as follows:

const fs = require('fs');

const file = fs.readFileSync('foo.txt').toString();

We can instead use methods like readFile rom fs.promise to read a file:

const fs = require('fs').promises;

(async () => {

const file = await fs.readFile('foo.txt')

const content = file.toString();

console.log(content);

})()

This way, we run asynchronous code in a sequence as we do with synchronous code without holding up the rest of our program.

Conclusion

If we want to create strings for file paths, we should use the path.join or path.resolve methods to create full paths.

This way, we won’t have to worry the path structure difference in different OSs.

The best way to store config settings is to put them all in a configuration and then reference everything from that file.

process.exit shouldn’t be used to exit the program when we encounter errors because it end the program abruptly, giving our program no chance of responding.

Finally, other than small scripts, we shouldn’t be using synchronous methods in our Node apps.