If you have a working Nodejs project, its features are limited to whatever the code implements,
and whatever its dependencies deliver. But Node allows you to change the runtime behavior
in a limited way by preloading
a specific module before running the program. Just load the
additional functionality using -r
option and enjoy additional behavior without any code changes.
This blog post shows several useful examples of such runtime extension.
Extending built-in methods
Nodejs console
object has a pair of very useful methods for measuring duration. Just start the
timer for a specific label and then end it - the duration in milliseconds will be printed to
the console.
1 | console.time('foo') |
But what if you call foo
multiple times? It would be very useful to get average / min / max
and standard deviation of multiple measurements. It would be very simple to implement this change
to the standard console.time
method behavior; but what if you already have a working code
and do not want to modify the existing code? Module preload to the rescue.
Take the existing application and preload
time-stats. The console.time
method will be
extended but no other modifications will be necessary.
1 | npm i time-stats |
This particular implementation keeps all measurements for each label and displays min, max, median and mean values. It even displays an up or down arrow to show if the latest duration is smaller or larger than the previous mean value.
Of course, if you like this behavior, you can easily include it in the "standard" source code
to avoid using -r time-stats
every time one starts the program. Just load the module
first before any other code!
1 | require('time-stats') |
Securing the Node module cache
NodeJS is a very flexible environment. You can change methods on existing objects, or set properties for methods that do not exist yet, and then intercept them, etc. The loaded modules cache is an important part of the NodeJS runtime and an Achilles' heel of its security. Any module can modify an already loaded module, set traps for modules to be loaded or crawl through all modules to discover sensitive methods like "login" and intercept them. How can we secure the NodeJS module cache?
A preloader that "freezes" and controls the access to the module cache to the rescue!
A 3rd party module you can preload controls the module.cache
and makes it "read-only"
for example, would disallow any "after-load" crawling and tampering. It is simple to implement
using ES6 Proxies, but there are ways to implement the same features using ES5.
See Playing havoc with Node module system
for more details. NodeSource even offers a commercial product for controlling what modules can
execute the "dangerous" calls, see Blacklisting Node.js Core Modules.
Monitoring running system
We do not have to modify the system's behavior; we can just observe it. For example, by preloading a module that instruments every module loaded later on we can turn an ordinary server into a live code coverage producer.
In the animation below I am running a Nodejs application with a preloaded module that makes a real time WebSocket server and instruments the desired source files. Then any client can connect and observe in real time executed statements.
No modifications to the existing source code were necessary; this was purely -r
runtime
option. See blog post Turning code coverage into live stream
for more details.
Module preloading limitations
Unfortunately, the external module preloading has the same limitation as the regular require
call - is is synchronous. Whatever the preloaded module does, it has to be synchronous. This
eliminates an entire class of useful features, like starting a server or making a remote call.