100 days of Angular 2 - Day 5: Bringing Express into the project

Today I'm going to bring Express into my Angular 2 project. This will let me write server side logic and an API for communicating with the client side code (all of the Angular 2 stuff). I'll also eventually hook up a database as well.

Right now my project is structured as shown below:

Basically, there are 3 main folders:

app/ contains Typescript source code, .css and .html

config/ just contains Webpack configuration settings

dist/ is where the transpiled/bundled/uglified code ends up after running Webpack

Make way for the server

All of the code in the project up until now was client side stuff. After adding Express into the mix, the project looks like this:

It's similar to using the Express generator except I don't need the view engine configuration (or the jade dependency), or the views/ folder. I did include a route for the API which will handle any server side logic down the road.

The bin/www file just contains some boilerplate setup such as defining the port, loading in the settings from app.js and starting the server.

The app.js file is pretty standard for Express apps, and includes the all important static middleware:

app.use(express.static(path.join(__dirname, '../dist')));

which refers to the dist/ folder that Webpack (in production mode) bundles the files and outputs to.

I did get a nasty error about not setting a default view engine

stephen@ MINGW64 ~/git/100-days-of-angular (master)
$ set DEBUG=express:* & node ./server/bin/www
[1] 12156
GET / 304 9.098 ms - -
GET /favicon.ico 500 6.881 ms - 1307
Error: No default engine was specified and no extension was provided.
    at new View (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\view.js:62:11)
    at EventEmitter.render (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\application.js:569:12)
    at ServerResponse.render (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\response.js:960:7)
    at C:\Users\stephen\git\100-days-of-angular\server\app.js:47:7
    at Layer.handle_error (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\layer.js:71:5)
    at trim_prefix (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\index.js:310:13)
    at C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\index.js:280:7
    at Function.process_params (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\index.js:330:12)
    at next (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\index.js:271:10)
    at Layer.handle_error (C:\Users\stephen\git\100-days-of-angular\node_modules\express\lib\router\layer.js:73:5)

But all the guides online said Express didn't need a view engine specified when using the static middleware that I was using.

It turns out just adding the favicon.ico to that static directory made everything better.

stephen@ MINGW64 ~/git/100-days-of-angular (master)
$ set DEBUG=express:* & node ./server/bin/www
[1] 9524
GET / 304 13.779 ms - -
GET /favicon.ico 200 3.854 ms - 318

Multiple static asset directories

However, moving the favicon into the dist/ folder isn't a long term solution, because doing a production build wipes out the dist/ folder with the following command:

"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"

The solution is to have Express use multiple directories as static asset directories. The docs say:

To use multiple static assets directories, call the express.static middleware function multiple times.

Express looks up the files in the order in which you set the static directories with the express.static middleware function.

So the solution here is to use something like this in my app.js:

app.use(express.static(path.join(__dirname, '../dist')));
app.use(express.static(path.join(__dirname, '../public')));

So Webpack bundles and uglifies all my Angular 2 code and sticks it in dist/ which is fine to delete and rebuild at any point.

And the more long term files can live in public/ which would include things like a favicon, images, or site wide CSS.

The production build + deployment

So now that I have Express involved, I have a more robust way of hosting my Angular 2 application. On the server, a build would look something like this:

$ git pull
$ npm run build

Once Webpack transpiles, bundles and uglifies the Angular 2 files, deployment is just starting the Express server with forever:

$ export NODE_ENV=production
$ forever start ./server/bin/www

I have a reverse proxy (nginx) sitting between the world wide web and this Express/Angular2 application, so that's what would handle gzip compression.

Note that I haven't dealt with any sort of "high availability uptime", meaning there's a brief delay when I take the currently running application down and bring the latest version up. Users would get a 404 or 500 error if they tried to visit at that point in time. I'll address this in the future for this application, or you can read a previous article I wrote about solving this before.

But what does a dev environment look like with Express / Angular 2 / Webpack?

This is a little trickier when since I want to do both front end (Angular 2) and back end (using Express) development at the same time, with hot reloading to get fast feedback.

Bad solution

A less than ideal solution would be to run the webpack transpiler/bundler/uglifier and start the Express app after that, but since that takes 15 - 20 seconds after each change, it's not a good solution for a development environment.

Less bad solution

Let's see, another option is to keep the existing dev webpack settings running, like so

webpack-dev-server --inline --progress --port 8085

And also keep the Express server running and reloading with something like:

nodemon ./server/bin/www

Nodemon watches changes to files and will restart a script. I'd have to set up a configuration file so Nodemon only watched things in the server/ folder and restarted it.

This would let me change a few things in the back end, like how a response gets sent back for a particular API, or tweak a database query, etc, and would reload just the server without affecting the front end (Angular 2) code at all. Similarly, the webpack-dev-server would ignore changes to the back end and only recompile/reload front end stuff if there's changes there.

However, a big downside is that the front end dev server would run on one port, say 8085, and the back end API and server side logic would run on a different port, say 8086.

This creates two problems:

  1. I'd have to deal with cross-origin requests and same-origin policies. This isn't too awful, but it's one more thing to set up in dev and remember to turn off in production.

  2. I'd have to define which port to use on every request. The Angular application would have to be smart enough to request HTML/JavaScript/CSS on port 8085 and request anything from the /api/* route on port 8086. Again I'd have to remember to turn this off in a production setting.

A good solution

If only there was a way for the webpack-dev-server to forward requests from the /api/* route to my Express server! Time to take a look at the webpack docs.

Aha! Here it is - Proxy requests in Webpack dev server. Looks to do exactly what I need it to. I'll just add this to my webpack.dev.js configuration file

devServer: {
    proxy: {
        '/api': {
            target: 'http://localhost:8086',
            secure: false
        }
    }
}

Let's test it out!

I created a route on the server side, located at ./server/routes/api.js which just contains a few lines:

var express = require('express');
var router = express.Router();

router.get('/test', function(req, res, next) {
  res.json({'testMessage': 'everything is going to be ok', 'someNumber': 457});
});

module.exports = router;

Then on the client side, I created a button in one of my components vegetables.component.html

<button (click)="testServer()">Test the server</button>
<div *ngIf="testMessageFromServer">
    <pre >server says: {{testMessageFromServer}}</pre >
</div>

And the controller just delegates to the service vegetables.component.ts

testServer(): void {
    this.vegetableService.getTestMessage().then(response => this.testMessageFromServer = response);
  }

Which actually makes the call to the server vegetable.service.ts

getTestMessage(): Promise<string> {
    return this.http.get('api/test')
    .toPromise()
    .then(response => response.json().testMessage)
    .catch(this.handleError);
}

I start the webpack-dev-server in one console and nodemon ./bin/server/www in another, thereby running, watching, and hot reloading the client and server separately. In a picture, it looks like this:

And there's the perfect development setup! The last thing to do is to tweak the nodemon watching/hot reloading so it only watches things in the server/ directory, which from looking at the docs appears to be a command line parameter:

nodemon --watch server ./server/bin/www

I'm really happy with this configuration for a dev environment. Auto watching and reloading the client separately from the server allows for fast feedback when I'm working on the front or the back end, or both of them together. And I don't need to turn off anything or do something special when it's time for the production build.

Day 5 summary

Webpack and the webpack-dev-server make for a great combination. I really like how I can make the dev server do all of the Typescript transpilation / bundling / serving of Angular 2 client side stuff, and then proxy certain requests to my Express back end for server side work.

You shouldn't have to restart the entire stack (front and back end) to see changes that only occurred on one side!

I also wrapped up the last part of the official Angular 2 tour of heroes tutorial which covered the Angular 2 HTTP service. Instead of using the mock in-memory web API I brought Express into the project and created a real backend.

Now that I have all of the plumbing and architecture set up, I might be able to focus on learning Angular 2 tomorrow. See day 6 here.

As a reminder, you can see all of my code on GitHub.