Like any kind of apps, JavaScript apps also have to be written well.
Otherwise, we run into all kinds of issues later on.
In this article, we’ll look at some best practices we should follow when writing Node apps.
Handle Errors
If there’s any chance that an error may occur with a piece of code, we should handle the error.
For example, if we have a change of promises, we should call catch
with an error handler callback passed into it.
We can write:
doSomething()
.then(doNextStage)
.then(doMoreThins)
.then(updateInterestedParties)
.then(cleanUp)
.catch(errorHandler);
We have the catch
method with the errorHandler
callback function passed into it.
The callback will be called when any of the promises in the chain throws an error.
Ensure our App Automatically Restarts
We should ensure that our app automatically restarts.
This way, we won’t have to do this ourselves when our app encounters an unhandled error.
We need a process manager like Nodemon, forever, or PM2 to do this.
The last 2 are for production usage.
Nodemon is used for development more often.
For instance, we can install PM2 globally with:
npm install pm2 -g
Then we can launch our Node app with:
pm2 start app.js
Cluster our App to Improve Performance and Reliability
We can course our app so that we have more than one instance of it.
This way, we can distribute the work across all cores.
We need this since Node apps run in a single process.
PM2 lets us do this easily by running:
pm2 start app.js -i max
This lets us run multiple processes equal to the number of cores present in the server.
The processes don’t share memory or resources.
They’ll all have their own connection to the database, for instance.
Therefore, we should use something like Redis to keep session state across all instances.
Require All Dependencies Upfront
It’s a good idea to have our require
calls all at the top so we won’t have to look for them in various parts of our code.
Also, we can spot any issues with them earlier.
For example, we can write:
const datastore = require("db")(someConfig);
app.get("/my-service", (request, response) => {
db.get(req.query.someKey)
// ...
});
We call require
at the top so that we run them all earlier.
If there’re any errors with them, they’ll be thrown at the beginning.
Also, require
is synchronous, so if it takes a long time to run, it’ll hold up our app.
If there any problems like these, we’ll find them early if we require them early.
Use a Logging Library to Increase Errors Visibility
Errors can happen in any environment.
And we want them to be visible so that we can fix them.
console.log
is very limiting in a production setting.
A better logging library can give us more control over how to log.
We can have various types of logging or save the data somewhere.
Some good libraries include Loggly and Winston. Winston is the base package for basic logging.
And Node-Loggly uses Winston for logging.
This way, we’ll see the errors in one central place that’s searchable.
Conclusion
Error handling can be made easy with a few changes to our code.
We should catch errors and log them so we can fix them is needed.