SECRET OF CSS

4 Ways to Use Dynamic Remotes in Module Federation | by Oskari Rautiainen | Jul, 2022


How you can configure URLs for your federated remote modules dynamically.

0*CojpBHvb1AcQvSad
Photo by Mourizal Zativa on Unsplash

How can you deploy the same micro frontend application to testing, staging, and production environments? How can your application support on-premise, cloud, and hybrid deployments simultaneously? How can you scale multiple teams working on different parts of the architecture simultaneously? To introduce new remote applications dynamically?

The answer is dynamically defined remote apps.

In a previous post, we discussed configuring a simple React application using Webpack Module Federation in a host-remote configuration. When we defined the URLs for the remote applications, we used localhost URLs:

The above works great for local development, but this would not work if we deployed and hosted the application. Of course, we could replace the strings with production URLs — but then what happens to local development? Do we keep flip-flopping back and forth manually, hoping no one merges in the wrong URLs?

This pattern would also not work if you have multiple deployment stages with different URLs and environments. If we could not test remote apps in isolated environments in deployment pipelines, we would run into chaos and frustration as developers introduce breaking changes. Everyone would lose all confidence in the development flow.

Instead of localhostwe want to define URLs dynamically to point to the correct host depending on the environment.

Luckily Webpack Module Federation supports dynamically defining URLs for our remote applications. We are going to consider four solutions available to us:

  1. Environment variables
  2. Webpackexternal-remotes-plugin
  3. Promise Based Dynamic Remotes: docs
  4. Dynamic Remote Containers: docs

The most straightforward choice is using environment variables at build time. You can replace localhost in the Webpack config with environment variables locally or a deployment pipeline:

You can define each remote app’s URL as a local or hosted production deployment at build time. The advantage of this approach is its’ simplicity, but there is a problem. We need to build a new version for each environment to update the URLs. For our enterprise use cases and multiple deployment models, this would not work.

What if you could change the remote URLs at runtime?

Under the hood, Module Federation allows us to load remote containers dynamically. We’ll cover that in a minute. But first, there is a handy Webpack plugin developed by Zack Jackson, one of the creators of Module Federation, called external-remotes-plugin. It allows us to resolve the URLs at runtime using templating:

All you need to do is define window.appAUrl, and window.appBUrl within your application before you load any code from the remote applications.

Now you have the flexibility to define your URLs however you want. For example, you could maintain a map of the host URLs to remote URLs for each environment, use server-side rendering to allow the backend to define the URLs, create a custom configuration management service, and many other methods.

This approach is fully dynamic and would solve our use cases, but there is still a slight limitation with this approach. We do not have complete control over the loading lifecycle.

Module Federation allows us to define the remote URLs as promises instead of URL strings. You can use any promise as long as it fits the get/init interface defined by Module Federation:

Within the promise, we create a new script tag and inject it into the DOM to fetch the remote javascript file. window.appAUrlcontains the URL for the remote app.

The Webpack remote configuration must still be in string format, so I’ve defined the promise separately from the config and stringified it later for better readability. Unfortunately, this approach is not the easiest to debug or maintain because it is stringified code inside of a config file.

There is another limitation with all of the approaches so far. We are limited to only loading the RemoteA and RemoteB apps, which could be a security advantage. But what if a developer is working on a new remote app that we don’t know exists yet? What if a partner or customer wants to inject their remote module into their deployment of our app?

Module Federation allows us to load remote applications programmatically without needing to define any URLs in our Webpack config:

But how?

Similar to the above approach, before you try to load any remote apps, you first need to fetch the remote module using a dynamic script tag. Then you can manually initialize the remote container.

From the Webpack docs:

container is referring to a remote app we configured in the "remotes" field in our host app’s Webpack configuration.

module refers to one of the items we defined in the "exposes" field in our remote app’s Webpack configuration.

Using the approach of injecting a script tag you can fetch the remote container and store it in window.someContainer as long as the code resolves to the same get/init pattern we used earlier.

When you want to use one of the modules exposed by our remote app, all you need to do is call: container.get(moduleName) like in the example above.

What does this look like if you want to load a React remote app as we did in our basic React host-remote app?

Check out one of my other posts for an example:

Using dynamically loaded remotes, you’ve learned four different mechanisms to break down your micro frontend deployment. When you deploy our micro frontend, you can fetch your remote applications from any URL you define. You can deploy it to multiple test environments, on-premises, or in the cloud. Developers can choose whether to use production versions of other remote applications or to introduce new ones dynamically.

Which method would you choose?



News Credit

%d bloggers like this: