Node.js, a platform built on Chrome’s JavaScript engine helps to develop fast, scalable network application. It uses an event-driven, non-blocking I/O model which makes node.js lightweight and efficient. Node.js has become one of the most popular platforms over the last couple of years. It is of course very easy to get started on those Node.js projects but once you get beyond the basic “Hello World” application, knowing how to deal with errors and managing the right structure of your code seems a nightmare.
Let us look at few of the best Node.js practices that can protect you from the common Node.js traps:
1. Use Functional Inheritance
Prototypal inheritance is when objects inherit from other objects. The class operator also gets added to the language with ES6. Avoid debugging and understanding prototypal inheritance or classes and make use of functional inheritance like some richest Node contributors do. With simple function factory pattern and use of prototype, new or this, you can easily implement functional inheritance.
Below is the code by TJ Holowaychuk, the genius behind Express, Connect, Mocha and dozens of other Node modules. Express uses functional inheritance.
exports = module.exports = createApplication; // ... function createApplication() { var app = function(req, res, next) { app.handle(req, res, next); }; mixin(app, EventEmitter.prototype, false); mixin(app, proto, false); app.request = { __proto__: req, app: app }; app.response = { __proto__: res, app: app }; app.init(); return app; }
2. npm – Is A Blessing
npm is like a blessing to the node.js developers therefore always start your projects with npm init. Node Package Manager is the root of almost all the deployment systems for node.js. The simple dependable package management has allowed Node ecosystem grow extremely well. Till data 62,961 npms exists and they can help you in doing interesting stuff and keeps your code simple. Most people are just familiar with the NPM as a way to install dependencies but it is not only restricted to this but it is much more than this. Below it is explained in detail:
First create a new project using npm init, like so:
$ mkdir my-new-project $ cd my-new-project $ npm init
A new package.json gets created which allows you to add a bunch of metadata. This will help others working on the project to have the same setup as that of yours.
3. Use Util And Config File Separately
Always make it a habit of using util.js file. The functions which are to be used frequently must be written in util.js file. Use this file as a variable in your module. Doing this will reduce the number of global variables and code length.
Make sure you make a config file which stores your constant parameters. This can be well understood that if you want to show the details of top five students. There are chances that it can be increased to ten tomorrow, so don’t hard code the limit value. Do keep it in config file.
One of the best thing which you can do is to maintain a ‘lang’ folder This is useful for multilingual application.
Acquiring file is simple in Node.js. Here you go:
var util = require(‘./util.js’), englishMessages = require(‘./lang/en.js’);
4. Use Granular Modules As Much As You Can
Always try to break your code. Node.js allows you to work with many tables, therefore, it is suggested to make different modules for different tables. Also, make another parent module for interacting within the child modules.
This can be detailed out in a better way. There are several standards in a school’s database, for example, many sections which make the standard and many students make a section. Hence, you can structure this project in four modules.
- a. Student
- b. Section
- c. Standard
- d. School
Here school is the parent module and other three are child modules. School module will pass the controller to proper module. For example, if you want to update student’s table you need to update both section and standard table. Send the request to school module and then school module will transfer the request to the preferred module. This will help in sorting up code which will make easy to review when an error occurs.
Don’t acquire other modules into student module. Pass the request to School module, and then school module will transfer the request to the preferred module. It will help you to sort up your code and will make it easy to review when the error occurs.
5. Make Use Environment Variables
Configuration management has always been an important concern whenever we talk about any language. Optimally decoupling code from database and services which it has to use during development, QA and production need attention. It is suggested to use environment variables in Node.js. Look up the values from process.env in your code. Like, to figure out which environment you are running on, check the NODE_ENV environment variables:
console.log("Running in :" + process.env.NODE_ENV);
You must use environment variables even for the early stages of a project for ensuring there is no emission of sensitive and confidential information. This will also allow you to build the code properly from the beginning.
6. Choosing Wisely Among Event Handler And Callback
Generally, developers are confused which is better to use, event handler or callback? Usually, developers face a dilemma that when an event is executing a function on success or failure of a process then why not use callback? Let us have a deep discussion on Callbacks and Event Handlers.
6(a)- Callbacks
Callbacks are used when you have an asynchronous operation which needs caller a notification about its’ completion. Let’s see this code function:
var toppings = {cheese: true, bacon: true}; function orderPizza(data, done) { // send info to server (which might take a few milliseconds to complete) xhr({ uri: '/order/submit', data: data, complete: function(status, msg, response) { if (status !== 200) { // notify about error done(new Error(status +': '+ msg)); } // notify about success done(null, response); } }); } // the method "showOrderStatusDialog" will be called after we get the response // from the server orderPizza(toppings, showOrderStatusDialog); function showOrderStatusDialog(err, response) { if (err) { showOrderFailure(err);
From the above code, we can derive that callbacks are beneficial for cases where you have an action that triggers a direct reaction viz in batch processing, Ajax and animation.
6(b)- Event Handler
An event handler is a type of callback. Event handler is called whenever any event occurs. Such phrase of event handler is normally used in terms of user interfaces where events are like clicking somethings, moving the mouse and so on.
A callback is procedure which you pass as an argument to another procedure. And event handler is a procedure which is called when an event happens. Such event can be a callback also.The events and callback can be comprehended in better way.
- Events – Think of a Server (as Employee) and Client (as Boss).One Employee can have many Bosses. Whenever a task is finished, the employee raises the event and the bosses may decide to listen to the employee event or not. Here the employee is the publisher and the bosses are subscriber.
- Callback – Here in callback, the Boss specifically asks an employee to do a task and once the task is done, the Boss wants to be notified. In this employee, must make sure that once the task is done, he notifies only to the boss who requested, not necessary all the bosses. The employee will not notify the boss if the job is partially done. If only one boss has requested the information an employee will post a reply to one boss only and the notification is sent to the boss only when all the task is done.
Therefore, after having clear understanding of event and callback, developers must decide wisely which to use when.
7. Handling Errors
You must always ensure that your node.js application is error free because errors bring down your entire application. Good exception management is very important for any application and the best way to deal with the errors is to use the async structures. We can understand it in a better way that promises provide a .catch() handler will generate all errors to be dealt with. For example you have a chain of promises and and any one of them can suddenly fail, you can easily handle the error like:
doSomething() .then(doNextStage) .then(recordTheWorkSoFar) .then(updateAnyInterestedParties) .then(tidyUp) .catch(errorHandler);
The above example explains that it does not matter which of the earlier functions could have failed. Any error will end up in the errorHandler.
8. Automatic Restart Of node.js Application
Even after following all the best practices of handling errors there could be a chance some error may bring your application down. This is where it is important to ensure you must use a process manager to make sure the application recovers gracefully from a runtime error. The other scenario is when you need to restart it, is when entire server you are running on went down. In that situation, you want minimal downtime and for your node.js application to restart as soon as the server is alive again!
9. Testing Is Crucial
On production applications it is quite critical to get notified if something goes wrong. It could happen that you do not want to check your feeds and thousands of angry users tells you that your server is down and your node.js application is broken since last few hours. Hence, it is imperative to tool for alerting you for your critical behaviour. Some of the best performance monitoring tools for node.js are Loggly and NewRelic. Developers must have handy tools for monitoring performance of node.js applications.
Adieu
So these are some best practices of node.js application development according to me. If you would like to nominate an additional best practice you may please add them as your valuable comments. Let’s together develop more and more scalable node.js projects 🙂
References: codementor.io, risingstack.com, innofied.com, millermedeiros.com, stackoverflow.com, sitepoint.com