How to use Express to serve a frontend compiled by Webpack?

Viewed 1502

I have the following Express app which serves static files, specifically public/index.html.

server.js

const express = require('express');
const app = express();
const port = 8000;

app.use(express.static('public'));

app.listen(port, () => {
    console.log(`Example app listening @ http://localhost:${port}`);
});

public/index.html

<html>

    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    </head>

    <body>

        <script src="./dist/bundle.js"></script>

        <script>
            // possibly make API calls to http://localhost:8000
        </script>

    </body>

</html>

This HTML file requires a script located at ./dist/bundle.js.

The bundle is compiled using Webpack, which has this configuration:

webpack.config.js

const path = require("path");
const webpack = require("webpack");
const TerserPlugin = require("terser-webpack-plugin");

module.exports = {
    entry: "./src/index.js",
    mode: "development",
    module: {
        rules: [
            {
                test: /\.(js)$/,
                exclude: /(node_modules)/,
                loader: "babel-loader",
                options: { presets: ["@babel/env"] }
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    },
    resolve: { extensions: ["*", ".js"] },
    output: {
        path: path.resolve(__dirname, "public/dist/"),
        publicPath: "/dist/",
        filename: "bundle.js"
    },
    devServer: {
        contentBase: __dirname,
        port: 3000,
        publicPath: "http://localhost:3000/dist/",
        hot: true
    },
    plugins: [new webpack.HotModuleReplacementPlugin()],
    optimization: {
        minimize: true,
        minimizer: [new TerserPlugin()]
    }
};

As you can see in output.path, the bundle's compiled output should go into public/dist which is inside the static file directory served by Express. That way, index.html can access it.

Now the problem is, my development workflow is incredibly inefficient.

  1. webpack --mode development to write the latest bundle.js into public/dist

  2. node server.js to re-run the server (in case there were any server-side changes)

If I were using webpack-dev-server alone without my own backend, I would get all the nice benefits of hot reloading, HMR, etc, and I don't have to worry about recompiling.

I know I can run webpack-dev-server and server.js concurrently on two different ports, but index.html would be served from server.js and wouldn't be able to access the in memory dist/bundle.js served by the webpack dev server.

Is there a way to serve a Webpack-compiled frontend from an Express server and still get all the nice benefits of using Webpack-dev-server, specifically hot reloading?

Thanks!

0 Answers
Related