Angular and Express build tooling

In my previous post I deployed the Angular build artifacts to the Express server with a good ol’ copy-paste. This works the first time, but is definitely not a great solution for something seeing continuous development. Surely there’s got to be a better way! Where does that leave us? This is the part where theory and practice collide, but I contend its in a good way that makes us think about what we’re doing and what we want to do.

While developing the client:

  1. We want to take advantage of the live reloading and tooling in general available to Angular client development
    • We can accomplish this with the very handy ng serve --watch
  2. We want the Angular client to talk to the server, whether we’re actively developing the server or not
    • Likewise the Angular CLI has this built in: ng serve –proxy http://localhost:3000 this means we’re still using the Angular development server to actually serve the application, but any API calls are being forwarded to our real server – Express.
  3. We want to be able to deploy the client easily to our server so we can try it in production mode
    • Not quite sure what the solution is for this. We may need some additional tooling to support this case
    • Angular supports the --watch flag for ng build as well, this may be part of the solution

While developing the server:

  1. We want to monitor the server files and restart the server when any change
    • Surprise surprise, we’re not the first ones to think of this. nodemon does exactly this.
    • yarn add --dev nodemon
    • Instead of yarn start, just invoke nodemon in min-auth/min-auth-server/
  2. If we’re not developing the client, we want to use the most recent build of the client
    • Whatever the solution is here, its likely the the same as the client side desire #3 above
  3. We want to be able to deploy the server easily
    • I haven’t even started looking at this yet…

Move production Angular artifacts to Express server

This is the crux of the integration – how do we get production artifacts created by the Angular build process into Express so that Express can serve them up? A side effect of the whole motivation of decoupling technologies like this is that the solution may not be plug-and-play. What do we actually want to do here?

  • Clean any old versions of our client lying around
  • Build the Angular application artifacts
  • Copy the Angular artifacts into Express

Seems easy enough! Sticking with the Node tooling, we may as well do this with JavaScript. In the root min-auth/ folder, we can do:

$ yarn init

And answer all the questions that follow:

yarn init v0.18.1
question name (min-auth):
question version (1.0.0):
question description:
question entry point (index.js):
question git repository (https://github.com/tobymurray/min-auth.git):
question author (Toby Murray <email@email.email>):
question license (MIT):
success Saved package.json
Done in 14.42s.

This is conceptually creating a package with both min-auth-server and min-auth-client inside it, which is exactly what we need to integrate the two.

Node build script

We can do everything in JavaScript, so why not? Note that this is intended to be closer to the “quick and dirty” implementation than the “robust multi-platform multi-technology” tool. We’ll make a build.js to encapsulate the actual build logic, although there’s not much to it, so it probably wouldn’t be crazy to do it inline either.

So as to not re-write the wheel, we’ll use fs-extra. Add it to your local dependencies:

yarn add fs-extra --dev

Now use it!

// min-auth/build.js

var fse = require('fs-extra');
var p = require("path");

const PUBLIC_FOLDER = p.join("min-auth-server", "public");
const DIST_FOLDER = p.join("min-auth-client", "dist");

// Delete anything that's hanging around in the public folder, as right now we have nothing we want to keep around
fse.removeSync(PUBLIC_FOLDER)

// Copy the build artifacts from Angular to Express
fse.copySync(DIST_FOLDER, PUBLIC_FOLDER)

Then we can add some scripts to the package.json to make this a bit more convenient:

// min-auth/package.json - exclude this as JSON has no comments

{
  "name": "min-auth",
  "version": "1.0.0",
  "main": "build.js",
  "repository": "https://github.com/tobymurray/min-auth.git",
  "author": "Toby Murray <email@email.email>",
  "license": "MIT",
  "scripts": {
    "build-client": "cd min-auth-client && yarn && ng build --prod --aot && cd ..",
    "deploy-client": "yarn && node build.js",
    "build": "yarn build-client && yarn deploy-client && cd .."
  },
  "devDependencies": {
    "fs-extra": "^1.0.0"
  }
}

Serve it up!

Give it a shot – in min-auth invoke yarn run build:

$ yarn run build
yarn run v0.18.1
$ yarn build-client && yarn deploy-client
yarn build-client v0.18.1
$ cd min-auth-client && ng build --prod --aot
Hash: 0ff826d3d954c66776c3
Time: 23700ms
chunk {0} main.26330a263e64118bcace.bundle.js, main.26330a263e64118bcace.bundle.map (main) 45.1 kB {2} [initial] [rendered]
chunk {1} styles.1a2c149cb14baba676ab.bundle.css, styles.1a2c149cb14baba676ab.bundle.map, styles.1a2c149cb14baba676ab.bundle.map (styles) 1.69 kB {3} [initial] [rendered]
chunk {2} vendor.c94b330d9f9311379542.bundle.js, vendor.c94b330d9f9311379542.bundle.map (vendor) 1.69 MB [initial] [rendered]
chunk {3} inline.34fbd289d5d0c10e3d2b.bundle.js, inline.34fbd289d5d0c10e3d2b.bundle.map (inline) 0 bytes [entry] [rendered]
Done in 28.69s.
yarn deploy-client v0.18.1
$ node build.js
Done in 0.54s.
Done in 30.10s.

Run it the same old way:

cd min-auth-server && SET DEBUG=min-auth-server:* & yarn start
yarn start v0.18.1
$ node ./bin/www
 min-auth-server:server Listening on port 3000 +0ms

Or why not add a convenience method in package.json now that we have it?

// 
"scripts": {
  "start-server": "cd min-auth-server && nodemon",
  ...

We can now start the server and watch for changes with with yarn run start-server. We can also keep going, adding tasks and combining them into other tasks, but we can do all this as it comes up.

Alright! We can develop the client side in Angular, and independently develop the server side in Express, as well as build the client side in a consistent and automated fashion, deploying it with Express… we’ve just about got the bare minimum required to get this stack working effectively!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s