Webpack is all the rage today. It calls itself a module bundler, but it is so much more than that. It will replace your gulp setup entirely, as it will do your transformations, you bundling, your replacements, your asset compilation, just about everything on the front-end build chain.
But what about the backend?
This question bothered me, so I decided to go ahead and build something to find out.
The building blocks
Node JS
Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js is mainly used for backend and build chain tasks. For more information, visit https://nodejs.org/en/ .
ExpressJS
Express is a project of the Node.js Foundation, it is a robust web framework for NodeJS. The Express framework allows building backend services with REST-APIs.
For more detailed information, look here : https://expressjs.com/
WebPack
Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.
In this project, we use webpack to transpile and bundle the NodeJS code written in typescript.
For more detailed information, look here : https://webpack.js.org/
I am using version 3.10.0
Typescript
TypeScript is a free and open-source programming language developed and maintained by Microsoft. It is a strict syntactical superset of JavaScript, and adds optional static typing to the language. (from Wikipedia)
For more detailed information, look here : https://www.typescriptlang.org/
Setting up the project
Before we can start, we need to setup a regular node project.
The $ npm init
command comes in very handy here.
$ npm init This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (my.project) version: (1.0.0) 0.1.0 description: Some project entry point: (index.js) ./dist/index.js test command: git repository: keywords: author: muckibu.de license: (ISC) MIT About to write to /Users/gxc9gk6/my.project/package.json: { "name": "my.project", "version": "0.1.0", "description": "Some project", "main": "./dist/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "muckibu.de", "license": "MIT" } Is this ok? (yes)
as we already know we want to use Webpack, Express and typescript for our project, we can easily add them via the following commands:
$ npm install express
Will install express Js as a runtime dependency of our software.
$ npm install webpack typescript @types/express -D
Will install Webpack and the Typescript compiler as a development dependency, next to the typescript type definitions for express. We are using devDependencies here, because these modules will be needed during development, NOT during runtime.
Your package.json
file should now look similar to the following snippet:
{ "name": "my.project", "version": "0.1.0", "description": "Some project", "main": "./dist/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "muckibu.de", "license": "MIT", "devDependencies": { "webpack": "^3.10.0", "@types/express": "^4.11.0", "typescript": "^2.6.2" }, "dependencies": { "express": "^4.16.2" } }
webpack tweak
A small but important tweak to our package.json
file is to add webpack as a script. This way, we avoid installing webpack globally but instead can use the command $ npm run build
to run webpack without the need of accessing its runables from within the node_modules/.bin
folder.
{ "name": "my.project", "version": "0.1.0", "description": "Some project", "main": "./dist/index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "build": "webpack" // @@@ NEW @@@ }, "author": "muckibu.de", "license": "MIT", "devDependencies": { "webpack": "^3.10.0", "@types/express": "^4.11.0", "typescript": "^2.6.2" }, "dependencies": { "express": "^4.16.2" } }
Webpack setup
Next, we need to understand how webpack works and how its configuration works.
Webpack is configured with its webpack.config.js
file and it can be run webpack
at the command line – if installed globally. Webpack takes an entry module, reads the entire dependency tree and bundles it all together – usually a single file.
We are going to do this for our backend code. Let’s start with this – far too -simple configuration, which tells it to take the entry point ./src/index.ts
and generate a file at ./dist/index.js
.
As thewebpack.config.js
file is a regular javascript file (nodeJs), we need export a module as follows:
var path = require('path'); module.exports = { // the main source code file entry: './src/index.ts', output: { // the output file name filename: 'index.js', // the output path path: path.resolve(__dirname, 'dist') } };
This configuration would take the ./src/index.ts
and write it to ./dist/index.js
– cool but not very useful so far.
Transpiling TypeScript
What we actually want to do is transpiling typescript code into regular javascript, which can finally be executed by nodeJS.
Webpack uses so called Rules
and Loaders
for these kind of tasks (Webpack Docs)
What we want to do is to use the so called ts-loader module we can obtain from the npm registry. This loader will allow to transpile each and every typescript file we input and which will be referenced in the dependency tree into regular javascript using the typescript compiler we installed earlier.
Let`s install the ts-loader module as a devDependency via npm:
$ npm install ts-loader -D
After compleation, we need to do two more things. First we need to let webpack know how to deal with typescript files, second we need to comfigure typescript (at least some fundamentals)
Thewebpack.config.js
file needs to be extended with a key for modules, which itself contains the rules key, which holds a set of rules to be applied. We want to tell webpack that for each and every typescript file, ts-loader shall be used. so we add the following code to our webpack.config.js
file:
..., module: { rules: [ // all files with a `.ts` extension will be handled by `ts-loader` { test: /\.ts$/, loader: 'ts-loader' } ] }
In addition to this plain and easy configuration, we also want to tell typescript (the compiler) to add sourcemaps to the output for easier debugging of our app. Therefore, we create a tsconfig.json
file in the projects root folder with the following content:
{ "compilerOptions": { "sourceMap": true } }
The Node specifics
What if it all would just work out of the box? Boring, right ? 😉
The target
As NodeJS uses its own module loader derived from CommonJs, we need to tell that to webpack, as otherwise it will use assume a client side module loader that nodeJS can not cater with. Again, webpack makes it very easy to configure its process for spitting out nodeJS compatible code by adding a configuration to the webpack.config.js
file.
Let`s add one more configuration key to the webpack.config.js
file:
..., target: "node"
The externals
As NodeJS comes with its own module loader, we want to exclude the node_modules
folder from being bundled with the application. Fortunately, webpack has a solution for us at hand, externals.
..., externals: nodeModules
As you can see in the snippet above, I am using a the object
notation for external configuration. The actual content of the nodeModules
variable is generated automatically, so that I do not have to fiddle around with with webpack.config.js
all the time I install or uninstall a node module.
var fs = require('fs'); var nodeModules = {}; fs.readdirSync('node_modules') .filter(function (x) { return ['.bin'].indexOf(x) === -1; }) .forEach(function (mod) { nodeModules[mod] = 'commonjs ' + mod; });
So I would just let the script find all the modules in node_modules
as a key value pair to the nodeModules
variable. In addition you will notice that I prefix 'commonjs '
to all the module names for node module loader compatibility.
The conclusion
There is absolutely no reason not to use webpack for backend code. Actually it is pretty amazing and smooth. So go for it and try it out.
I put a starter for you on github:
https://github.com/wzr1337/node.express.webpack.starter