Saturday, August 13, 2016

Top 10 Mistakes Node.js Developers Make_part 1


Introduction

Node.js has seen an important growth in the past years, with big companies such as Walmart or PayPal adopting it. More and more people are picking up Node and publishing modules to NPM at such a pace that exceeds other languages. However, the Node philosophy can take a bit to get used to, especially if you have switched from another language.

In this article we will talk about the most common mistakes Node developers make and how to avoid them. You can find the source code for the examples on github.

1 Not using development tools
  • nodemon or supervisor for automatic restart
  • In-browser live reload (reload after static files and/or views change)
Unlike other languages such as PHP or Ruby, Node requires a restart when you make changes to the source code. Another thing that can slow you down while creating web applications is refreshing the browser when the static code changes. While you can do these things manually, there are better solutions out there.

1.1 Automating restarts

Most of us are probably used to saving a file in the editor, hit [CTRL+C] to stop the application and then restart it by pressing the [UP] arrow and [Enter]. However you can automate this repetitive task and make your development process easier by using existing tools such as:
  • nodemon
  • node-supervisor
  • forever
What these modules do is to watch for file changes and restart the server for you. Let us take nodemon for example. First you install it globally:
  1.   npm i nodemon -g
Then you should simply swap the node command for the nodemon command:
  1.   # node server.js
  2.   $ nodemon server.js
  3.   14 Nov 21:23:23 - [nodemon] v1.2.1
  4.   14 Nov 21:23:23 - [nodemon] to restart at any time, enter `rs`
  5.   14 Nov 21:23:23 - [nodemon] watching: *.*
  6.   14 Nov 21:23:23 - [nodemon] starting `node server.js`
  7.   14 Nov 21:24:14 - [nodemon] restarting due to changes...
  8.   14 Nov 21:24:14 - [nodemon] starting `node server.js`
Among the existing options for nodemon or node-supervisor, probably the most popular one is to ignore specific files or folders.

1.2 Automatic browser refresh

Besides reloading the Node application when the source code changes, you can also speed up development for web applications. Instead of manually triggering the page refresh in the browser, we can automate this as well using tools such as livereload.

They work similarly to the ones presented before, because they watch for file changes in certain folders and trigger a browser refresh in this case (instead of a server restart). The refresh is done either by a script injected in the page or by a browser plugin.

Instead of showing you how to use livereload, this time we will create a similar tool ourselves with Node. It will do the following:
  • Watch for file changes in a folder;
  • Send a message to all connected clients using server-sent events; and
  • Trigger the page reload.
First we should install the NPM dependencies needed for the project:
  • express - for creating the sample web application
  • watch - to watch for file changes
  • sendevent - server-sent events, SSE (an alternative would have been websockets)
  • uglify-js - for minifying the client-side JavaScript files
  • ejs - view templates
Next we will create a simple Express server that renders a home view on the front page:
  1. var express = require('express');
  2. var app = express();
  3. var ejs = require('ejs');
  4. var path = require('path');

  5. var PORT = process.env.PORT || 1337;

  6. // view engine setup
  7. app.engine('html', ejs.renderFile);
  8. app.set('views', path.join(__dirname, 'views'));
  9. app.set('view engine', 'html');

  10. // serve an empty page that just loads the browserify bundle
  11. app.get('/', function(req, res) {
  12. res.render('home');
  13. });

  14. app.listen(PORT);
  15. console.log('server started on port %s', PORT);
Since we are using Express we will also create the browser-refresh tool as an Express middleware. The middleware will attach the SSE endpoint and will also create a view helper for the client script. The arguments for the middleware function will be the Express app and the folder to be monitored. Since we know that, we can already add the following lines before the view setup (inside server.js):
  1. var reloadify = require('./lib/reloadify');
  2. reloadify(app, __dirname + '/views');
We are watching the /views folder for changes. And now for the middleware:
  1.  var sendevent = require('sendevent');
  2.   var watch = require('watch');
  3.   var uglify = require('uglify-js');
  4.   var fs = require('fs');
  5.   var ENV = process.env.NODE_ENV || 'development';

  6.   // create && minify static JS code to be included in the page
  7.   var polyfill = fs.readFileSync(__dirname + '/assets/eventsource-polyfill.js', 'utf8');
  8.   var clientScript = fs.readFileSync(__dirname + '/assets/client-script.js', 'utf8');
  9.   var script = uglify.minify(polyfill + clientScript, { fromString: true }).code;

  10.   function reloadify(app, dir) {
  11.     if (ENV !== 'development') {
  12.       app.locals.watchScript = '';
  13.       return;
  14.     }

  15.     // create a middlware that handles requests to `/eventstream`
  16.     var events = sendevent('/eventstream');

  17.     app.use(events);

  18.     watch.watchTree(dir, function (f, curr, prev) {
  19.       events.broadcast({ msg: 'reload' });
  20.     });

  21.     // assign the script to a local var so it's accessible in the view
  22.     app.locals.watchScript = '<script>' + script + '</script>';
  23.   }

  24.   module.exports = reloadify;
As you might have noticed, if the environment isn't set to 'development' the middleware won't do anything. This means we won't have to remove it for production.
The frontend JS file is pretty simple, it will just listen to the SSE messages and reload the page when needed:
  1. (function() {

  2.     function subscribe(url, callback) {
  3.       var source = new window.EventSource(url);

  4.       source.onmessage = function(e) {
  5.         callback(e.data);
  6.       };

  7.       source.onerror = function(e) {
  8.         if (source.readyState == window.EventSource.CLOSED) return;

  9.         console.log('sse error', e);
  10.       };

  11.       return source.close.bind(source);
  12.     };

  13.     subscribe('/eventstream', function(data) {
  14.       if (data && /reload/.test(data)) {
  15.         window.location.reload();
  16.       }
  17.     });

  18.   }());
The eventsource-polyfill.js is Remy Sharp's polyfill for SSE. Last but not least, the only thing left to do is to include the frontend generated script into the page (/views/home.html) using the view helper:
  1.   ...
  2.   <%- watchScript %>
  3.   ...
Now every time you make a change to the home.html page the browser will automatically reload the home page of the server for you (http://localhost:1337/).

2 Blocking the event loop

Since Node.js runs on a single thread, everything that will block the event loop will block everything. That means that if you have a web server with a thousand connected clients and you happen to block the event loop, every client will just...wait.

Here are some examples on how you might do that (maybe unknowingly):
  • Parsing a big json payload with the JSON.parse function;
  • Trying to do syntax highlighting on a big file on the backend (with something like Ace or highlight.js); or
  • Parsing a big output in one go (such as the output of a git log command from a child process).
The thing is that you may do these things unknowingly, because parsing a 15 Mb output doesn't come up that often, right? It's enough for an attacker to catch you off-guard and your entire server will be DDOS-ed.

Luckily you can monitor the event loop delay to detect anomalies. This can be achieve either via proprietary solutions such as StrongOps or by using open-source modules such as blocked.

The idea behind these tools is to accurately track the time spend between an interval repeatedly and report it. The time difference is calculated by getting the time at moment A and moment B, subtracting the time at moment A from moment B and also subtracting the time interval.

Below there's an example on how to achieve that. It does the following:
  • Retrieve the high-resolution time between the current time and the time passed as a param;
  • Determines the delay of the event loop at regular intervals;
  • Displays the delay in green or red, in case it exceeds the threshold; then
  • To see it in action, each 300 miliseconds a heavy computation is executed.
The source code for the example is the following:
  1.  var getHrDiffTime = function(time) {
  2.     // ts = [seconds, nanoseconds]
  3.     var ts = process.hrtime(time);
  4.     // convert seconds to miliseconds and nanoseconds to miliseconds as well
  5.     return (ts[0] * 1000) + (ts[1] / 1000000);
  6.   };

  7.   var outputDelay = function(interval, maxDelay) {
  8.     maxDelay = maxDelay || 100;

  9.     var before = process.hrtime();

  10.     setTimeout(function() {
  11.       var delay = getHrDiffTime(before) - interval;

  12.       if (delay < maxDelay) {
  13.         console.log('delay is %s', chalk.green(delay));
  14.       } else {
  15.         console.log('delay is %s', chalk.red(delay));
  16.       }

  17.       outputDelay(interval, maxDelay);
  18.     }, interval);
  19.   };

  20.   outputDelay(300);

  21.   // heavy stuff happening every 2 seconds here
  22.   setInterval(function compute() {
  23.     var sum = 0;

  24.     for (var i = 0; i <= 999999999; i++) {
  25.       sum += i * 2 - (i + 1);
  26.     }
  27.   }, 2000);
You must install the chalk before running it. After running the example you should see the following output in the terminal:

As said before, existing open source modules are doing it similarly so use them with confidence:
If you couple this technique with profiling, you can determine exactly which part of your code caused the delay.
Written by Alexandru Vladutu

If you found this post interesting, follow and support us.
Suggest for you:

Complete Node JS Developer Course Building 5 Real World Apps

Node.js Tutorials: The Web Developer Bootcamp

Learn How To Deploy Node.Js App on Google Compute Engine

Learn and Understand NodeJS

Learn Nodejs by Building 12 Projects

No comments:

Post a Comment