TL;DR in this article we demonstrate how to:
- Create a minified bundle for a Node.js application (using Webpack 4).
- Create a self-contained executable (using Nexe).
- Create a Docker containers to run the application.
…without any configuration file!
Packaging, bundling, deploying Node.js applications can get very tricky when you are faced with the diversity of tools that can be used. Even when tools have been chosen, configuration is often non-trivial.
In this post we will see that packaging a Node.js application does not have to be difficult or confusing. By sticking to a minimal set of tools and using just the necessary configuration, we will be able to create the following artifacts: minified bundle, self-contained executable, Docker image.
All the code from this article can be found on GitHub.
Our demonstration application is performing a bandwidth test with
jsdom to avoid having to resort to a headless browser. The output looks like the following:
$ node index.js Starting bandwidth test... ~ Speed 0 ~ Speed 7.9 Mbps ~ Speed 35 Mbps ~ Speed 60 Mbps ~ Speed 60 Mbps ~ Speed 64 Mbps ~ Speed 65 Mbps ~ Speed 67 Mbps = Speed 66 Mbps
The implementation details are not really relevant, but long story short;
jsdom is loading the URL
https://fast.com. We then display the bandwidth estimation every second until the test is completed.
Bundling with Webpack
In the past, I found Webpack to be a bit cumbersome to use, even for simple use-cases. With version 4, I was delighted to see that they now provide meaningful defaults and simple options. In our case, it is not even necessary to have a configuration file, which is great!
Producing a minified bundle is as simple as:
$ webpack index.js --output bundle.min.js --mode production --target node
That’s it! This command will create a new file:
bundle.min.js in the current directory. Since the
--target is Node.js, we can invoke it directly:
If you want faster builds as well as a watch-mode, you can use the following variations of the previous command:
$ webpack index.js --output bundle.min.js --mode development --target node $ webpack index.js --output bundle.min.js --mode development --watch --target node
Yes, it’s that easy!
Compiling into a Single Executable
We could have stopped at our minified bundle, but what if we could get a self-contained executable packaging our application as well as all dependencies? And by dependencies, we really mean: Node.js itself and everything needed for the runtime.
With our already existing bundle
bundle.min.js, a single command is needed to create the executable:
$ nexe bundle.min.js -t alpine-x64-8.9.3 -o app
What we’re saying here is that our
bundle.min.js should be compiled with a Node.js runtime version 8.9.3 compiled for Alpine Linux x64 (which will be useful in a moment to create a Docker image). You can also check the exhaustive list of all possible targets
You should now have a new executable in your current folder:
app. It can be started by invoking it like any other command:
Building Docker Images
And now the cherry on the cake! Although you can already easily ship your self-contained application created using Nexe, why not create a Docker container out of it? This can be achieved with the following
FROM alpine:3.7 COPY ./app .
$ docker build -t app .
The resulting image should weigh around 44MB. And here is how to run the application:
$ docker run app ./app Starting bandwidth test... ... = Speed 66 Mbps
I will not pretend that the tools described here are able to handle all use-cases (or that they are the only possible tools; there are a plethora!), far from it. It is mostly restricted to simple Node.js applications (with dependencies). But this is probably a nice starting point for more complex situations, for which you might need to create a proper configuration file with more bells and whistles.