Building a Stable Node.js Project Architecture. Best Practices for Node.js Development
Often product development process which involves JavaScript, is accompanied by the use of Node.js, a JavaScript runtime environment. The birth of this technology has certainly turned the use of JS upside-down. Today, JavaScript is in the category of the most preferred languages to build apps thanks to Node.js.
What is so special about this technology? To answer this, let’s reflect on not only this technology’s benefits but also its architecture limitations and the ways to deal with cons.
Node JS brief history
Node.js was introduced by Ryan Dahl in 2009. The technology is mostly used for building app’s server side/ back-end development. What’s special about Node.js is that the technology is asynchronous.
This means that server continues to process other client requests without urging the client to wait till another previously sent request is processed. Let’s say, it’s Node.js “value proposition” for all who would like to create reliable JS-based apps.
What is Node JS commonly used for?
The non-blocking I/O machine behind the current framework is a great way to build real-time web app with NodeJS, mobile products, chats, data streaming apps, browser games, APIs and medium-performance JS apps.
Node JS real-time applications examples and showcase
Among the companies who rely their tech part to Node.js are LinkedIn, Yahoo, IBM, Netflix, PayPal, Uber and others.
Let’s see where else Node is used apart from back-end (2017 data):
As of business point of view, Node.js is used for:
If you have worked with Node.js, you probably already know that with Node.js you can gain the following:
- Since Node.js is created with the C++ help, you can call C functions
- Asynchronous nature which accelerates app’s functioning and provides the ability to multitask. Apart from this, non-closing i/o method suits for high-traffic, real-time websites, resources creation.
- Stable multiplatform app development
- Lots of Node.js development tools for better workflow: npm-s, Express, Socket.io, etc. (we’ll touch upon these a bit later in the article)
- Clear and flexible learning curve
But with Node.js architecture limitations you lose the opportunities to:
- Create heavy-computed apps with the elements of 3D projection; calculation apps.
As Node.js is single-threaded, it is not the right fit for such projects. All the actions happen on a single thread, and hence, overload CPU. For such types of apps or software, it’s better to utilize multithreading languages, like C, C#. For the full-scale video games, you can use Unity.
- Use some npm-s.
Not all npm-s, we’ve mentioned in the pros, are of high quality and stable. Thus, you have to filter them properly and choose only the reliable ones. (Author’s note: Think of npm-s, like of plugins in Wordpress).
- Taking in consideration Node.js architecture, you can’t utilize relational databases at full power.
Node.js come best with such document-oriented databases, like MongoDB
Let’s get into tech details and best practices for Node.js development and more detailed practical tips on working with this platform
Application Specifics
First and foremost, think about what type of application you plan to release. To further proceed with Node.js project architecture building, ask yourself some of the following questions:
- Are you going to build a real-time web app with Node.js? Is it meant to be a mobile or a console one? Or maybe it's a multiplatform app?
- What data should the app operate with? Are these databases, files, or remote storages (like Amazon S3)?
- Do you plan to use special software in your application? Are sophisticated data processing algorithms such as face detection or text recognition enlisted in your app's functionality business plan?
- Does your application need an extra hardware, like camera, microphone, various sensors, or any other related devices?
- What are the architectural specifics of the future app? Is this meant to be a client server, MVC or maybe any other type of architecture?
If you've answered all of these questions, make sure that Node.js is able to fully meet all requirements set for your project development. For example, if you need an API server that works with several types of databases, Node.js might be a good choice. But if you need an application that is designed to build 3D graphics using Directx, you might want to get acquainted with C ++ a bit closer.
Let's assume that your application uses special temperature and contamination sensors. You can pair such features with RaspberryPi, Arduino or any other special device to go further and create a 'smart' functionality model. But before you start, make sure that the driver of any mentioned device is compatible with Node.js.
Best practices for Node.js development workflow
Everyone in the development team is unique as well as their own code style. Therefore, it’s recommended to settle and take into account the following code organization nuances before the development stage.
- Functional development style or object-oriented programming patterns usage?
Since JS is a weakly-typed language and allows you to write your code in a freestyle, it's still better to agree upon a single code writing rules in your team. This will keep most of the misunderstandings away and will help your colleagues to get the better understanding of the project’s code.
- Code style
- Your team’s experience with the integration of third-party means and devices in your application (i.e. Google Maps, data collection, analytics tools, e-communication means,etc.)
- Data models you’re up to work with (files, databases or third-party APIs)
- Communication and data exchange tools you’re going to use (REST API, Blouse Protocol, Socket io, GraphQl, DDP protocol)
- Possibility to utilize 3rd party libraries (hardware libraries, special algorithms)
Discuss with your teammates code writing do-s and don’t-s. Check the quality of what is written, using such Node.js development tools, like lint
The scope of work and its specifics can be as random as possible. Let's say one of your teammates works with SQL database, someone else deals with Amazon API. Thus, each of your colleagues is assigned to do the particular tasks.
Don't reinvent the bike
Currently Node.js community is up and thriving. A lot of neat features are invented and written by other developers already. So before you create a particular functionality for your application, make sure if someone else has not encountered the exact problem before.
If you’re not the only one who has experienced a certain issue, you might find the solution in npm packages. This is an entire catalogue of many ready-made useful libraries that will make life much easier for you.
The same applies to frameworks. Think about whether to use any of them to speed up the process to build a real-time web app with Node JS or a mobile one.
Let's say if you’re dealing with REST API, you can try out Express js. If you need to interact with a particular database type, you can refer to such frameworks, as Mongoose js or SQL depending on which database type you need.
Although packages can benefit your project, there are some significant dangers to be aware of. Given the fact, that these solutions are open source there are several threats to bear in mind:
- Duplicates
There are too many packages already and some of them clone the others. Unfortunately, this mainstream is only growing. Be careful and make sure you choose the right and unique npm.
- Malicious code
Since these packages are not supervised, anyone can write anything they want. Read more about security issues in the article by David Gilbertson ‘I’m harvesting credit card numbers and passwords from your site. Here’s how’. So if your product has to provide AAA-security type check each code snippet of any package you instal meticulously.
Always stay ahead of the time
JS and Node.js community is constantly growing. ES standards are frequently updated. Old features are being replaced by the new, better ones and implemented in Node.js. Thus it’s important to always monitor the technology’s state of art.
For instance,
[calbackHell](http://callbackhell.com/)
fs.action(source, function (err, res) {
if (err) {
console.log('Error: ' + err)
} else {
res.acton(function(err, res) {
console.log('Error s: ' + err)
})
})
}
})
Was replaced with
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
fs.action(source)
.then(res => res.action())
.then(res => res.action())
.then(res => res.action())
.catch(err => console.log('Error : ' + err))
Now we can use async/await as the alternative to Promises:
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
fs.action(source)
.then(res => res.action())
.then(res => res.action())
.then(res => res.action())
.catch(err => console.log('Error : ' + err))
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
fs.action(source)
.then(res => res.action())
.then(res => res.action())
.then(res => res.action())
.catch(err => console.log('Error : ' + err))
As for now, community’s opinion whether to use promises or async/await vary.
Try to update your knowledge base with the new Node.js and ES releases on the regular basis. This will help you keep a modernized development process.
Node.js app development techniques and tips
“Deep in the human unconscious is a pervasive need for a logical universe that makes sense. But the real universe is always one step beyond logic.”
― Frank Herbert.
To overcome Node.js architecture limitations and trivial-to-challenging issues, keep to the clear development structure.
Since Node.js project might consist of only one file, this does not mean that you need to pile everything up into one great mess.
Make your code as readable and understandable for others as it could be. The following recommendations :
- Follow the instructions on the structure development for the framework you are using. If you do not use any, try to place your code in the directories and subdirectories in the most logical way possible.
- File naming
Keep to a single agreed file naming. For example, choose one of these: ErrorHandler
or errorHandler
orerror_handle
or error-handler
. Try to name the files according to their purpose but not according to their functionality. For example, it’s better to name the File NotifyAllUserByEmaisSMSLocal
as Notifier
.
- The entry point must not contain unnecessary code lines
The entry point is the main.js
,app.js
and www
files that are requested to launch your application. Such files contain only certain methods or classes calls, but not more.
index.js
.
Keeping only import / export in these files is in general considered to be a justified practice.
Tips for better Node.js project architecture
The way the code is written signifies the ‘face’ (reputation) of the programmer. It also shows how the entire development team deals with the app’s creation using a certain technology or language. Node.js in our case. Therefore, always try to keep it up-to-date and structured (and comprehensive for other developers). Code readability is one of the main ways to build stable, real time web app with Node.js.
- Use code quality control tools, like Lint
This tool will help you not to slip out/keep a keen eye on any trivial small error./Give a trivial error no chance. It will also allow you to keep the code in one unified form.
- Keep track of your files size
Too large files are difficult in guiding and understanding. The optimal file size is of 300-500 lines or less. So if you have noted that the code is constantly growing within the same file, turn it to the directory with several files inside.
- Comment on your code
When you write a universal module that will be used in several places, don’t forget to create a quick guide on how to utilize this code.
[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
fs.action(source)
.then(res => res.action())
.then(res => res.action())
.then(res => res.action())
.catch(err => console.log('Error : ' + err))
You can also leave instructions for methods in your code
/**
* Provide parse date for single format on project
* @param Date
* @example
* dateToString(date) => String
* **/
If you’re developing a particular REST API, you can embed instructions on its usage in the code itself. It’s recommended though, to create the complete documentation/guidelines and store it on a single resource.
/**
* Provide Api for Account
Account Register POST /api/v1/account/
@params
email {string}
password {string}
Account Login POST /api/v1/account/login
@params
email {string}
password {string}
Account Logout GET /api/v1/account/logout
@header
Authorization: Bearer {token}
**/
There are 2 sides of a coin though.
To ensure the use of Node.js architecture best practices, keep your code as descriptive and organized as possible but don’t overdo.
Don’t comment each code string. It will do more harm than good and nothing but only make the development process more complex. The better tip is to comment the code snippet’s purpose but not its functionality (what does it do). Depending on the development style (callback, promise, async/await) write and use only one (if it’s possible) general error handler/processor.
Errors handling
Errors handling is another important aspect among other best practices for Node.js development to bear in mind.
Since JavaScript is not as strict as Java all the responsibility lies on the development team.
First and foremost, always try to process the errors. Otherwise it can lead to app’s uncontrolled behavior.
- With callback
- With Promise
- With async / await
const withoutErrors = calback => (err, updatedTank) => {
if (err) {
return // do something
}
return calback(updatedTank);
};
fs.action(withoutErrors(data => ...))
const handlError = error => {
if (err) {
return // do something
}
};
fs.action()
.then(data => )
.then(data => )
.cattch(handlError)
class Actions
async action1 (data) {
return fs.action(data)
}
async action2 (data) {
return fs.action(data)
}
....
}
try {
await new Actions().action1();
await new Actions().action1();
} catach(error) {
return handlError(error)
}
Node JS development tools
A toolkit which allows to launch several apps simultaneously. It might be useful if you’d like to run several services at the same time with one command/request.
Hot reload feature for Node.js. This tool automatically updates/ resets your project after any code change is made. A quite handy tool during the Node.js project architecture development.
These two packages ensure app’s launch during the (OC) system’s start.
Provides with the opportunity to record app’s logs to the primary source (file or database). The package comes to help, when you need the app to work remotely and don’t have the full access to it.
A tool designed for the better work with threads.
To sum up with
We hope that Node.js architecture best practices represented in this article will help you reach the most desireable result when looking for the way to build performant real-time web or mobile apps with Node.js.