Josh F
Josh F

Reputation: 110

Angular 2 Webpack Production Build & Node: can't get direct URLs to serve

I followed the instructions to create a webpack build environment from the angular.io site: https://angular.io/docs/ts/latest/guide/webpack.html, and got it working. Then, I took the Tour of Heroes tutorial that I got working, and I migrated it to a webpack environment.

If I run the Tour of Heroes using the webpack dev environment (with npm start), it works great. If I then create a production build (with npm run build), and serve the resulting files with node, I cannot get the server to respond when I try to access a URL directly. Below I will outline the steps to create the environment and the problem step-by-step. I believe the solution is to modify my server.js file, but I don't know what is needed.

How to recreate:

  1. Create the webpack build environment per the instructions on the website (link above).
  2. Copy the app folder from the Tour of Heroes liteserver environment to the src folder of the webpack environment
  3. In index.html of the webpack environment, comment out (or remove) all the scripts for polyfills and Configure SystemJS. Index.html should look like this:

    <html>
      <head>
        <base href="/">
        <title>Angular 2 QuickStart</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!--
        <link rel="stylesheet" href="styles.css">
        <script src="node_modules/core-js/client/shim.min.js"></script>
        <script src="node_modules/zone.js/dist/zone.js"></script>
        <script src="node_modules/reflect-metadata/Reflect.js"></script>
        <script src="node_modules/systemjs/dist/system.src.js"></script>
        <script src="systemjs.config.js"></script>
        <script>
          System.import('app').catch(function(err){ console.error(err); });
        </script>
        -->
      </head>
      <!-- 3. Display the application -->
      <body>
        <my-app>Loading...</my-app>
      </body>
    </html>
    
  4. Modify the package.json so it has what is needed from both environments. Mine looks like this:

    {
      "name": "angular2-webpack",
      "version": "1.0.0",
      "description": "A webpack starter for Angular",
      "scripts": {
        "start": "webpack-dev-server --inline --progress --port 8080",
        "test": "karma start",
        "build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
      },
      "licenses": [
        {
          "type": "MIT",
          "url": "https://github.com/angular/angular.io/blob/master/LICENSE"
        }
      ],
      "dependencies": {
        "@angular/common": "~2.1.1",
        "@angular/compiler": "~2.1.1",
        "@angular/core": "~2.1.1",
        "@angular/forms": "~2.1.1",
        "@angular/http": "~2.1.1",
        "@angular/platform-browser": "~2.1.1",
        "@angular/platform-browser-dynamic": "~2.1.1",
        "@angular/router": "~3.1.1",
        "angular2-cool-storage": "^1.1.0",
        "angular2-in-memory-web-api": "0.0.20",
        "bootstrap": "^3.3.6",
        "core-js": "^2.4.1",
        "reflect-metadata": "^0.1.3",
        "rxjs": "5.0.0-beta.12",
        "zone.js": "^0.6.25"
      },
      "devDependencies": {
        "@types/core-js": "^0.9.34",
        "@types/node": "^6.0.45",
        "@types/jasmine": "^2.5.35",
        "angular2-template-loader": "^0.4.0",
        "awesome-typescript-loader": "^2.2.4",
        "css-loader": "^0.23.1",
        "extract-text-webpack-plugin": "^1.0.1",
        "file-loader": "^0.8.5",
        "html-loader": "^0.4.3",
        "html-webpack-plugin": "^2.15.0",
        "jasmine-core": "^2.4.1",
        "karma": "^1.2.0",
        "karma-jasmine": "^1.0.2",
        "karma-phantomjs-launcher": "^1.0.2",
        "karma-sourcemap-loader": "^0.3.7",
        "karma-webpack": "^1.8.0",
        "null-loader": "^0.1.1",
        "phantomjs-prebuilt": "^2.1.7",
        "raw-loader": "^0.5.1",
        "rimraf": "^2.5.2",
        "style-loader": "^0.13.1",
        "typescript": "^2.0.3",
        "webpack": "^1.13.0",
        "webpack-dev-server": "^1.14.1",
        "webpack-merge": "^0.14.0"
      }
    }
    
  5. From the webpack folder, run npm install

  6. From the webpack folder, run npm run build to create prod build files. This is what the webpack tutorial says to do.
  7. Create a node server environment.
  8. Create a server.js file at the root that looks like this:

    var express = require("express");
    
    var app = express();
    
    app.use(function(req, res, next) {
        console.log("Request recieved for:", req.url);
        next();
    });
    
    app.use(express.static('public'));
    
    app.use(function(req, res, next) {
        res.status(404);
        res.send('404 file not found');
    });
    
    app.listen(4040, function() {
        console.log("yes: 4040");
    }); 
    
  9. In the node environment, create a public folder, and put all the files from the dist folder that was created in step 6 into the public folder.

  10. Run the node server with node server.js
  11. Go to localhost:4040. Everything works fine when accessed this way.
  12. Enter this URL directly: http://localhost:4040/heroes
  13. Get the 404 Error.
  14. From the webpack environment if you run npm start, you can go to the URL directly http://localhost:4040/heroes and it works fine.

If you want to see all the code for this, here is the github repo: https://github.com/joshuaforman/heroeswebpack

Upvotes: 2

Views: 1079

Answers (1)

Josh F
Josh F

Reputation: 110

I've got it figured out. The issue was in the server.js. After more research, I found this: https://expressjs.com/en/starter/static-files.html. Need to add more middleware after the express.static middleware: app.use('/heroes', express.static('public'));. The direct URLs work as needed with this server.js file:

var express = require("express");

var app = express();

app.use(function(req, res, next) {
    console.log("Request recieved for:", req.url);
    next();
});

app.use(express.static('public'));
app.use('/heroes', express.static('public'));
app.use('/dashboard', express.static('public'));

app.use(function(req, res, next) {
    res.status(404);
    res.send('404 file not found');
});

app.listen(4040, function() {
    console.log("yes: 4040");
});

The app I'm working on also needs to pass params, so if you need to do that, you will need middleware of this format:

app.get('/peopledetails/:uid', function(req, res){
    var uid = req.params.uid,
        path = req.params[0] ? req.params[0] : 'index.html';
    res.sendFile(path, {root: './public'});
});

Thanks to this stack question for the above: Serve Static Files on a Dynamic Route using Express.

Thanks for the help, folks.

Upvotes: 3

Related Questions