NxModuleFederationPlugin
The NxModuleFederationPlugin
is a Rspack plugin that handles module federation in Nx Workspaces. It aims to provide the same Developer Experience(DX) that you would normally receive from using Nx's withModuleFederation
function.
Usage
To use the plugin, you need to add it to your rspack.config.ts
file. You can do this by adding the following to your config.
Client Side Rendering
1import {
2 NxModuleFederationPlugin,
3 NxModuleFederationDevServerPlugin,
4} from '@nx/module-federation/rspack';
5import config from './module-federation.config';
6
7export default {
8 ...otherRspackConfigOptions,
9 plugins: [
10 new NxModuleFederationPlugin({
11 config,
12 }),
13 new NxModuleFederationDevServerPlugin({
14 config,
15 }),
16 ],
17};
18
Server Side Rendering
1import {
2 NxModuleFederationPlugin,
3 NxModuleFederationSSRDevServerPlugin,
4} from '@nx/module-federation/rspack';
5import config from './module-federation.config';
6
7export default {
8 ...otherRspackConfigOptions,
9 plugins: [
10 new NxModuleFederationPlugin({
11 config,
12 isServer: true,
13 }),
14 new NxModuleFederationSSRDevServerPlugin({
15 config,
16 }),
17 ],
18};
19
How it works
The NxModuleFederationPlugin wraps and configures the Module Federation plugin from @module-federation/enhanced
to provide a streamlined experience in Nx workspaces. Here's what the plugin does:
Base Configuration: It sets up essential Rspack configurations:
- Disables runtime chunking (
runtimeChunk: false
) - Sets a unique name for the output
- Configures specific settings for server-side rendering when needed
- Disables runtime chunking (
Module Federation Setup: The plugin automatically:
- Configures the remote entry filename (
remoteEntry.js
) - Sets up exposed modules based on your configuration
- Manages remote module connections
- Handles shared dependencies and libraries
- Configures the remote entry filename (
Runtime Plugins: It supports additional runtime plugins, including special handling for Node.js environments when running in server mode.
Shared Libraries: The plugin includes a dedicated system for managing shared libraries across federated modules, helping to avoid duplicate code and ensure consistent versions.
You can learn more about how Nx handles Module Federation in the Module Federation and Nx Guide.
Deployment
How applications are deployed depends on the teams and organizational requirements. There are two approaches:
- À la carte deployments - Each application is deployed according to a release schedule, and can have different cadences.
- Affected deployments - When changes are merged, use Nx to test and deploy the affected applications automatically.
Often times, teams mix both approaches so deployments to staging (or other shared environments) are automatic. Then, promotion from staging to production occurs on a set cadence (e.g. weekly releases). It is also recommended to agree on a process to handle changes to core libraries (i.e. ones that are shared between applications). Since the core changes affect all applications, it also blocks all other releases, thus should not occur too frequently.
You may also choose to fully automate deployments, even to production. This type of pipeline requires good end-to-end testing to provide higher confidence that the applications behave correctly. You will also need good rollback mechanisms in case of a bad deployment.
When figuring out the best deployment strategy, or even how to achieve it with Module Federation and Nx, it is worth understanding what is happening under-the-hood. Each host and remote in your Module Federation system is treated like a separate application. However, when a remote is loaded into a host via Module Federation, Module Federation itself does not make any kind of distinguishment. It only cares that the JS file it is trying to load is available at the pre-specified URL, such that it can make a network request to fetch the JS file.
When working locally, Nx parses the config in module-federation.config.ts
to determine what projects in the workspace are federated. It then reads the project graph for these projects to determine what port they will be served on and uses this information to form a URL to tell Module Federation where it will find the remote JS files. You'll commonly see the following configuration in your module-federation.config.ts
:
1export default {
2 remotes: ['shop', 'cart'],
3};
4
These names match the names of the projects in the workspace. Therefore, Nx can find them in the project graph and determine the information it needs. This usually amounts to Nx creating the following URLs:
❯
shop@localhost:4201
❯
cart@localhost:4202
When it comes deployment to a real server, you'll need to configure the URLs to point to their real location. For example, if you deploy the host and the remotes to three servers, each with their own domain, you'd need to configure that:
❯
shop@https://shop.example.com
❯
cart@https://cart.example.com
The remotes
option in the module-federation.config.ts
file allows you to do this:
1export default {
2 remotes: [
3 ['shop', 'https://shop.example.com'],
4 ['cart', 'https://cart.example.com'],
5 ],
6};
7
However, once you make this change, it will no longer work locally. You need some mechanism to change the URLs when you're building for a production environment.
A simple way to achieve this could be to use an environment variable:
1const remotes = process.env.PRODUCTION_DEPLOY
2 ? [
3 ['shop', 'https://shop.example.com'],
4 ['cart', 'https://cart.example.com'],
5 ]
6 : ['shop', 'cart'];
7
8export default {
9 remotes,
10};
11
In your CI/CD pipeline, you can set the PRODUCTION_DEPLOY
environment variable to true
and then build for production, and the remotes will be configured to point to their real location.
API Reference
NxModuleFederationPlugin
1export class NxModuleFederationPlugin {
2 constructor(
3 private _options: {
4 config: ModuleFederationConfig;
5 isServer?: boolean;
6 },
7 private configOverride?: NxModuleFederationConfigOverride
8 ) {}
9}
10
ModuleFederationConfig
1export interface ModuleFederationConfig {
2 /**
3 * The name of the module federation application.
4 */
5 name: string;
6 /**
7 * The remotes that the module federation application uses.
8 */
9 remotes?: Remotes;
10 /**
11 * The library type and name the ModuleFederationPlugin uses to expose and load the federated modules.
12 */
13 library?: ModuleFederationLibrary;
14 /**
15 * The federated modules to expose for consumption by host/consumer applications.
16 */
17 exposes?: Record<string, string>;
18 /**
19 * A function that allows you to configure shared libraries.
20 * This function is called for each shared library that is used by the module federation application.
21 * The function is passed the library name and the shared library configuration.
22 * If the function returns `undefined` the default shared library configuration is used.
23 * If the function returns `false` the shared library is not shared.
24 * If the function returns a shared library configuration object, that configuration is used.
25 */
26 shared?: SharedFunction;
27 /**
28 * Additional shared libraries that are shared by the module federation application.
29 * This is useful when you want to share a library that is not found as part of the direct dependencies of the application found in the Nx Graph.
30 */
31 additionalShared?: AdditionalSharedConfig;
32 /**
33 * `nxRuntimeLibraryControlPlugin` is a runtime module federation plugin to ensure
34 * that shared libraries are resolved from a remote with live reload capabilities.
35 * If you run into any issues with loading shared libraries, try disabling this option.
36 */
37 disableNxRuntimeLibraryControlPlugin?: boolean;
38}
39
40export type Remotes = Array<string | [remoteName: string, remoteUrl: string]>;
41export type ModuleFederationLibrary = { type: string; name: string };
42export type SharedFunction = (
43 libraryName: string,
44 sharedConfig: SharedLibraryConfig
45) => undefined | false | SharedLibraryConfig;
46export interface SharedLibraryConfig {
47 singleton?: boolean;
48 strictVersion?: boolean;
49 requiredVersion?: false | string;
50 eager?: boolean;
51}
52export type AdditionalSharedConfig = Array<
53 | string
54 | [libraryName: string, sharedConfig: SharedLibraryConfig]
55 | { libraryName: string; sharedConfig: SharedLibraryConfig }
56>;
57
NxModuleFederationConfigOverride
1/**
2 * Used to override the options passed to the ModuleFederationPlugin.
3 */
4export type NxModuleFederationConfigOverride = Omit<
5 moduleFederationPlugin.ModuleFederationPluginOptions,
6 'exposes' | 'remotes' | 'name' | 'shared' | 'filename'
7>;
8