2

I'm trying to run my React app locally using React, Node, Webpack 2. Whenever I hit a route that isn't / I get a 404. My goal is to be able to run my node server, have webpack-dev-server run, use browserHistory and back my webpack historyApiFallback work.

What currently does work:

  1. If I just run webpack-dev-server and no node server then browserHistory works fine, no 404s.
  2. If I run node with hashHistory it works fine, no 404s.

So that rules out that my routes aren't working. Here is some code:

server.js

const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema');

const app = express();

app.use('/graphql', expressGraphQL({
  schema,
  graphiql: true
}));

const webpackMiddleware = require('webpack-dev-middleware');
const webpack = require('webpack');
const webpackConfig = require('../webpack.config.js');
app.use(webpackMiddleware(webpack(webpackConfig)));

app.listen(process.env.PORT || 5000, () => console.log('Listening'));

webpack.config.js

const webpack = require('webpack');
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

const VENDOR_LIBS = [
  'axios', 'react', 'react-dom', 'react-router', 'react-apollo', 'prop-types'
];

module.exports = {
  entry: {
    bundle: './client/src/index.js',
    vendor: VENDOR_LIBS
  },
  output: {
    path: path.join(__dirname, 'dist'),
    publicPath: '/',
    filename: '[name].[chunkhash].js'
  },
  module: {
    rules: [
      {
        use: 'babel-loader',
        test: /\.js$/,
        exclude: /node_modules/
      },
      {
        test: /\.scss$/,
            use: [{
                loader: "style-loader"
            }, {
                loader: "css-loader"
            }, {
                loader: "sass-loader"
            }]
      },
      {
        test: /\.(jpe?g|png|gif|svg|)$/,
        use: [
          {
            loader: 'url-loader',
            options: {limit: 40000}
          },
          'image-webpack-loader'
        ]
      }
    ]
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest']
    }),
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
    }),
    new HtmlWebpackPlugin({
      template: './client/src/index.html'
    })
  ],
  devServer: {
    historyApiFallback: true
  }
};

routes.js

import React from 'react';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';

import App from './components/App';
import Portal from './components/portal/Portal';

const componentRoutes = {
  component: App,
  path: '/',
  indexRoute: { component: Portal },
  childRoutes: [
    {
      path: 'home',
      getComponent(location, cb) {
        System.import('./components/homepage/Home')
          .then(module => cb(null, module.default));
      }
    }
  ]
};

const Routes = () => {
  return <Router history={ browserHistory } routes={ componentRoutes } />
};

export default Routes;

Again, the goal is to be able to locally start up my node server, use browserHistory and not get 404s. I don't want to use hashHistory and I need to use my node server so I can use graphql. I also don't want to revert back to webpack v1. Though here is a link to where people got it working in v1:

historyApiFallback doesn't work in Webpack dev server

Roman Pokrovskij
  • 7,969
  • 14
  • 68
  • 119
user2465134
  • 6,278
  • 5
  • 23
  • 39
  • Try this `historyApiFallback: { index: '/' }` – Roman Maksimov Apr 17 '17 at 21:00
  • @RomanMaksimov still doesn't work – user2465134 Apr 17 '17 at 21:33
  • I've recall that the `webpack-dev-server` ignores the `devServer` config options. You have to specify them manually instead like there [https://github.com/webpack/webpack-dev-server/blob/master/examples/node-api-simple/server.js](https://github.com/webpack/webpack-dev-server/blob/master/examples/node-api-simple/server.js). But this is for `webpack-dev-server` module, I'm not sure about `webpack-dev-middleware`, though you should try as it also gets extra options. – Roman Maksimov Apr 18 '17 at 00:07

1 Answers1

3

The historyApiFallback option is specifically for webpack-dev-server. If you're running your own server, even with webpack-dev-middleware, you need to configure it to send the index.html when a 404 occurs. Because you're using html-webpack-plugin the index.html you want to send does not exist on your file system but only in memory. To make it work you can access the output of the webpack compiler as shown in the comment of html-webpack-plugin #145.

server.js

const path = require('path');
const express = require('express');
const expressGraphQL = require('express-graphql');
const schema = require('./schema');

const app = express();

app.use('/graphql', expressGraphQL({
  schema,
  graphiql: true
}));

const webpackMiddleware = require('webpack-dev-middleware');
const webpack = require('webpack');
const webpackConfig = require('../webpack.config.js');
const compiler = webpack(webpackConfig);

app.use(webpackMiddleware(compiler));
// Fallback when no previous route was matched
app.use('*', (req, res, next) => {
  const filename = path.resolve(compiler.outputPath, 'index.html');
  compiler.outputFileSystem.readFile(filename, (err, result) => {
    if (err) {
      return next(err);
    }
    res.set('content-type','text/html');
    res.send(result);
    res.end();
  });
});

app.listen(process.env.PORT || 5000, () => console.log('Listening'));
Michael Jungo
  • 25,186
  • 3
  • 62
  • 71
  • Any idea on why I would get a `DevTools failed to parse SourceMap: http://localhost:5000/shallowEqual.js.map` error? – user2465134 Apr 18 '17 at 01:09
  • 1
    No sorry, I'm not even sure where this is coming from, because `webpack-dev-middleware` should handle the source maps. You could try using a different [source map](https://webpack.js.org/configuration/devtool/) e.g. `devtool: 'inline-source-map'`. But if they come from your file system, you would need to configure express to handle them as well (as static files) instead of going to the fallback route. – Michael Jungo Apr 18 '17 at 01:26