Reputation: 33
I am right now working on a dummy project to learn express and webpack with react and react router, I want to redirect all of my server requests to index.html so I won't get "Cannot GET" errors when accessing different URLs. Right now I am in development mode and the problem is I am serving generated HTML using HtmlWebpackPlugin. I tried this code, but all I get is "ENOENT" errors when accessing any URL except the root one.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../build/index.html'), function(err) {
if (err) {
res.status(500).send(err);
}
});
});
Here's also my webpack.config :
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './client/index.js',
output: {
path: path.join(__dirname, 'build'),
publicPath: '/',
filename: 'bundle.js',
},
module: {
rules: [
{
use: 'babel-loader',
test: /\.js$/,
exclude: /node_modules/,
},
{
use: ['style-loader', 'css-loader'],
test: /\.css$/,
},
],
},
plugins: [
new HtmlWebpackPlugin({
template: 'client/index.html',
fileName: path.join(__dirname, 'build/index.html'),
}),
],
};
Is it possible to solve this issue by using express?
P.S. Also can someone explain why does server loads generated index.html normally when accessing root URL. I thought I am intercepting all my requests with the code snippet above. Really confused right now
Here's my server.js file:
const express = require('express');
const models = require('./models');
const expressGraphQL = require('express-graphql');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const schema = require('./schema/schema');
const path = require('path');
const app = express();
const MONGO_URI = 'some_uri';
mongoose.Promise = global.Promise;
mongoose.connect(MONGO_URI);
mongoose.connection
.once('open', () => console.log('Connected to MongoLab instance.'))
.on('error', error => console.log('Error connecting to MongoLab:',
error));
app.use(bodyParser.json());
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.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../build/index.html'),
function(err) {
if (err) {
res.status(500).send(err);
}
});
Here's package.json, I am using older versions of some packages because I am following the tutorial, plan to update them later though:
{
"name": "graphql-learning-project-02",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "nodemon index.js --ignore client"
},
"author": "",
"license": "ISC",
"dependencies": {
"apollo-client": "^0.8.1",
"axios": "^0.15.3",
"babel-core": "^6.22.1",
"babel-loader": "^6.2.10",
"babel-preset-env": "^1.1.8",
"babel-preset-react": "^6.22.0",
"body-parser": "^1.16.0",
"connect-mongo": "^1.3.2",
"css-loader": "^0.26.1",
"express": "^4.14.0",
"express-graphql": "^0.6.1",
"express-session": "^1.15.0",
"graphql": "^0.8.2",
"html-webpack-plugin": "^2.26.0",
"lodash": "^4.17.4",
"mongoose": "^4.7.8",
"nodemon": "*",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"react": "15.4.2",
"react-apollo": "^0.9.0",
"react-dom": "15.4.2",
"react-router": "^3.0.2",
"react-router-dom": "^4.3.1",
"style-loader": "^0.13.1",
"webpack": "^2.2.0",
"webpack-dev-middleware": "^1.9.0"
}
}
Upvotes: 1
Views: 987
Reputation: 81
If anybody is still experiencing this issue, you are mostly using express with webpack-dev-middleware and react-router just like me.
I found a solution when using the latest updates of the packages.
You can notice in webpack-dev-middleware documentation that writeToDisk option is set to false by default. Just set it to true while still sending the index.html file located in your build folder.
For those of you also needing a proxy to use a REST api on localhost:80, here's the server script I came up with :
import express from 'express';
import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from '../webpack.config';
import proxy from 'express-http-proxy';
global.watch = true;
const compiler = webpack(webpackConfig);
const app = express();
const getPath = req => require('url').parse(req.url).path;
const createProxy = ({ hostname = 'localhost', path = '' }) => proxy(hostname, { proxyReqPathResolver: req => `${path}${getPath(req)}` });
export default async () => {
// Webpack dev Server
app.use(webpackDevMiddleware(compiler, {
writeToDisk: true,
noInfo: true,
stats: webpackConfig.stats,
publicPath: webpackConfig.output.publicPath
}))
// Hot module replacement
.use(webpackHotMiddleware(compiler))
// Proxy
.use('/api', createProxy({ path: '/api' }))
.use('/auth', createProxy({ path: '/auth' }))
// Static path
.use(express.static(webpackConfig.output.path))
// Handles routes
.get('/*', (req, res) =>{
res.sendFile(webpackConfig.output.path + '/index.html');
})
// Start
.listen(3000, (err) => {
if (err) {
console.error(err);
} else {
console.info('Listening to http://localhost:3000');
}
});
};
I am using babel-node to run the scripts so use require instead of import if using node.
Upvotes: 0
Reputation: 7496
You can use the connect-history-api-fallback middleware to always fallback to index.html. This can be used in both dev and prod environments.
Use it like this:
const connectHistoryApiFallback = require('connect-history-api-fallback');
...
app.use(connectHistoryApiFallback({
verbose: false
}));
// static files and folders must be set after connectHistoryApiFallback
app.use("/", express.static(__dirname));
app.listen(app.get('port'), (err) => {
console.info('Node app is running on port', app.get('port'));
});
Upvotes: 0
Reputation: 11
webpack-dev-middleware is not generating any files.
It means no index.html
is generated.
According to its documentation:
No files are written to disk, rather it handles files in memory
Please remove:
app.get('*', (req, res) => { ...
To server webpack files in the development mode:
app.use('*', webpackMiddleware(webpack(webpackConfig)))
app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../build/index.html') ...
can be used after production webpack build.
Upvotes: 1