When to use nodeJS
Single-threaded: Therefore it doesn’t need to allocate memory for each thread and there is no cost of switching between threads.
Being single-thread might be an advantage in some cases, but heavy computation could choke up Node’s single thread and cause problems for all clients since they all use the same thread.
Not a good fit for
- CPU heavy applications.
- Using NodeJS with a relational database is still quite a pain. Do yourself a favour and pick up any other environment like Rails, Django, or ASP.Net MVC if you’re trying to perform relational operations.
A good fit for
- Real-time applications.
- Non-blocking, event-driven apps.
- When data fits into an object storage such as MongoDB.
The chat application is really the sweet-spot example for NodeJS: it’s a lightweight, high traffic, data-intensive (but low processing/computation) application that runs across distributed devices. It’s also a great use-case for learning too, as it’s simple, yet it covers most of the paradigms you’ll ever use in a typical NodeJS application.
API for K-V Database
API on top of an object (key-value) DB. Although NodeJS really shines with real-time applications, it’s quite a natural fit for exposing the data from object DBs (e.g. MongoDB). JSON stored data allow NodeJS to function without the impedance mismatch and data conversion.
For instance, if you’re using Rails, you would convert from JSON to binary models, then expose them back as JSON over the HTTP when the data is consumed by Backbone.js, Angular.js, etc., or even plain jQuery AJAX calls. With NodeJS, you can simply expose your JSON objects with a REST API for the client to consume. Additionally, you don’t need to worry about converting between JSON and whatever else when reading or writing from your database (if you’re using MongoDB).
If you’re receiving a high amount of concurrent data, your database can become a bottleneck. As depicted above, NodeJS can easily handle the concurrent connections themselves. But because database access is a blocking operation (in this case), we run into trouble. The solution is to acknowledge the client’s behavior before the data is truly written to the database. With that approach, the system maintains its responsiveness under a heavy load, which is particularly useful when the client doesn’t need firm confirmation of a the successful data write.
Typical examples include: the logging or writing of user-tracking data, processed in batches and not used until a later time; as well as operations that don’t need to be reflected instantly (like updating a ‘Likes’ count on Facebook) where eventual consistency (so often used in NoSQL world) is acceptable. Data gets queued through some kind of caching or message queuing infrastructure—like RabbitMQ or ZeroMQ—and digested by a separate database batch-write process, or computation intensive processing backend services, written in a better performing platform for such tasks. Similar behavior can be implemented with other languages/frameworks, but not on the same hardware, with the same high, maintained throughput.
In more traditional web platforms, HTTP requests and responses are treated like isolated event; in fact, they’re actually streams. This observation can be utilized in NodeJS to build some cool features. For example, it’s possible to process files while they’re still being uploaded, as the data comes in through a stream and we can process it in an online fashion. This could be done for real-time audio or video encoding, and proxying between different data sources (see next section).
NodeJS is easily employed as a server-side proxy where it can handle a large amount of simultaneous connections in a non-blocking manner. It’s especially useful for proxying different services with different response times, or collecting data from multiple source points.
An example: consider a server-side application communicating with third-party resources, pulling in data from different sources, or storing assets like images and videos to third-party cloud services.
Server Side Web Application with a Relational DB Behind
Comparing NodeJS with Express.js against Ruby on Rails, for example, there used to be a clean decision in favor of the latter when it came to accessing relational databases like PostgreSQL, MySQL, and Microsoft SQL Server.
Relational DB tools for NodeJS were still in their early stages. On the other hand, Rails automatically provides data access setup right out of the box together with DB schema migrations support tools and other Gems (pun intended). Rails and its peer frameworks have mature and proven Active Record or Data Mapper data access layer implementations.
The case of Threads
Threads are a way for systems to run multiple operations concurrently. So every request would open a new thread, every thread had all it required to run the same code to completion. Let’s use this analogy.
Imagine a restaurant with just one person serving food. When the demand for food increases, things would crazily go wrong. People would have to wait till all preceding orders were delivered. A way to solve this problem would be to employ more people to serve food right? This way, more customers would be served at once.
Each thread is a new employee and the browsers, well, hungry people. I’m sure you can get the point now.
But this system has a downside. It would get to a point where there’s a lot of requests and starting up new threads would consume a whole lot of memory and system resources. Just like in our example, employing more and more people to serve food would cost more money and space.
But it’s also a good thing that immediately the server responds to the browser, the connection is closed and all resources (memory etc) are retrieved.
A benefit of this system is that it excels at CPU intensive applications. CPU intensive operations require a lot of logic and take more time to compute. Since every new request is handled by a new thread, it takes serious work off the main thread therefore making the system faster and able to perform important computations.
Imagine we have this multi-threaded server (running Ruby on rails) that reads a file saved on the server and sends it to the requesting browser. It’s important to understand that Ruby won’t read the file for us directly. Ruby will tell the file system to read the file and return the contents . The file system is a program used to store and retrieve data on a computer.
The point is, Ruby just sits there doing nothing till the file system is done. And then Ruby collects the content and sends the contents to the browser.
Here’s where NodeJS comes in. In Node, while the file system is reading the file, Node uses the idle time to handle other requests. When the file system is done, it’s smart enough to tell Node to come take the resource and send to the browser. This is possible because of Node’s event loop.
Unlike other languages that require a new thread or process for every single request, NodeJS takes all requests and then delegates most of the work to other system workers. There’s something called a Libuv library which handles this work effectively with help from the OS kernel. When the background workers are done doing their work, they emit events to NodeJS callbacks registered on that event.
This brings us to callbacks. They are basically functions passed into other functions as arguments and are called when certain conditions occur.
So what NodeJS developers basically do is write event handlers that get called when certain Node events happen.
NodeJS is extremely faster than multi-threaded systems. Yeah even with a single thread.
This is because programs usually don’t only consist of numeric, arithmetic and logic computations that take much time. In fact, a lot of times they merely write something to the file system, do network requests or access peripheries such as the console or an external device. Node excels in situations like this. When it experiences them, it quickly delegates the work to someone else and tackles other incoming requests.
Let’s us an analogy in case you didn’t quite grasp 100%.
Imagine a king that has thousands of other servants, the king writes a list of things he wants the servant to do (a long list), a servant takes it and delegates work to all other servants. When one is done, he takes his result to the king. The king is always busy making other lists while the servants were working, so he collects his result and gives him another work to do.
The point is, the king does one thing at a time even if a lot of things are running parallel. In this analogy, the king is your code and the servants are NodeJs background workers. Everything is happening parallel except your code.
In Node, blocking operations are the root of all evil—99% of Node misuses come as a direct consequence.