Webpack Module Federation for production environnement
Tags: webpack, javascript, tutorial, microfrontends
Published: 2022-02-26 11:31:24

Webpack Module federation is a really nice piece of technology for decoupling your micro frontends setups, with dynamic shared code resolution. All the pitfalls of webpack externals gone.  
But i see a lot of problematic examples on how to setup ModuleFederation with toy setups that don't work for production environnement or even even worse, hacky solutions thats no one can understand without understanding webpack arkanes.
Here on dev.to, check [this one](https://dev.to/waldronmatt/tutorial-a-guide-to-module-federation-for-enterprise-n5). Even the tagged update with promise example from the MF author himself  is a problematic setup.  
It's problematic, because these setups are not Website URL agnostic or rely on runtime hacks to inject your integration / staging / production URLs. These setups don't scale even if they claim they do so.  
Here i'm going to show a non problematic setup example, that do not need runtime hacks or configurations. 

A simple `configure once, build once, deploy everywhere` setup.
Sounds like magic, doesn't it. Let's dive in.

## Architecture

![micro-frontend architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivsxvhbp4z81bxcgzbdy.png)

The first thing you should notice, is that everything is behind the same domain URL. So the fist recommendation of this setup is :
- Put your website behind one and only one Domain URL.
  - No CORS configuration nightmare.
  - It hides your internal architecture from the end-user. your users should have no idea that your website is split into micro-frontends.
  - It simplifies your webpack configuration with module federation.

To hide your internal architecture from the user, use a reverse Proxy. It can be any technology, like Nginx, Kong, HaProxy, kubernetes ingress, ... 

## From one domain to URL agnostic setup

How does this setup simplifies webpack configuration with module federation ?  
Some of you might already know this. It's the same recommandation you have always been told as frontend developpers. So here is the second recommandation for this setup :
- Use relavite path URLs for your own website URLs. Only use domain URLs for external websites.
  - domain URL: `https://domain/mfe-1`
  - relavite URL: `/mfe-1`

So, it's really that simple. Module Federation is webpack, and webpack supports relative URL setup. 
By using relative URL, you won't need to configure your website Domain anywhere and you'll be able to deploy it everywhere. The same build, deployed on your integration, staging and production environnments.

## Webpack example config for Host

So here is a `webpack.config.js` example for your Host using only only relative URL. No harcoded Domain. The important parts are in:
- 'publicPath' attribute that should use relative URL,
- 'remotes' property in ModuleFederation setup that should also use relative URL

```javascript
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const deps = require('./package.json').dependencies;
const webpack = require('webpack');

module.exports = {
  mode: 'production',
  entry: '<path to your local host index.js>',
  cache: false,
  devtool: false,
  optimization: {
    minimize: true,
  },
  output: {
    // you could use 'auto', but 'auto' does not work with ie11, it's better to use relative url anyway.
    publicPath: '/',
    clean: true,
    pathinfo: false,
    path: buildPath,
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'swc-loader',
        options: {
          cacheDirectory: true,
        },
      },
    ],
  },
    : [
        new ModuleFederationPlugin({
          name: 'host',
          remotes: {
            // relative URL
            mfe_1: 'mfe_1@/mfe-1/remoteEntry.js',
            mfe_2: 'mfe_2@/mfe-2/remoteEntry.js',
          },
          shared: {
            ...deps,
          },
        }),
        new HtmlWebpackPlugin({
          template: 'index.html',
          inject: 'body',
          hash: true,
          minify: true,
        }),
      ],
};
```

## Webpack example config for a micro-frontend

So here is a `webpack.config.js` example for your Micro-frontend using only only relative URL. No harcoded Domain. The important parts are in:
- 'publicPath' attribute that should use relative URL,

```javascript
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const deps = require('./package.json').dependencies;
const webpack = require('webpack');

module.exports = {
  mode: 'production',
  entry: '<path to your local mfe index.js>',
  cache: false,
  devtool: false,
  optimization: {
    minimize: true,
  },
  output: {
    // relative URL
    publicPath: '/mfe-1/',
    clean: true,
    pathinfo: false,
    path: buildPath,
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(js|jsx|ts|tsx)$/,
        exclude: /node_modules/,
        loader: 'swc-loader',
        options: {
          cacheDirectory: true,
        },
      },
    ],
  },
  plugins: [
        new ModuleFederationPlugin({
          name: 'mfe_1',
          filename: 'remoteEntry.js',
          library: { type: 'var', name: 'mfe_1' },
          remotes: {},
          exposes: {
            './view1': '<path to your local view js>',
          },
          shared: {
            ...deps,
          },
        }),
      ]
};
```

## Conclusion

You should not need to understand the arkanes of webpack to configure module federation for your production environnement.
Just remember these two rules :
- Put your website behind one and only one Domain URL.
- Use relavite path URLs for your own website URLs. Only use domain URLs for external websites.

Thanks for reading. Don't forget to `like` the article if it was usefull to you.