Module Federation en Node.js, de forma sencilla

Module Federation es compatible con Node.js de serie. Tanto si solo consumes módulos en runtime como si lo integras en un pipeline de build con Webpack/Rspack, puedes adoptarlo con relativamente poca configuración. Este documento recorre las formas más habituales de usar Module Federation en Node.js.

Descripción general

En una aplicación de servidor Node.js puedes cargar módulos remotos mediante Module Federation. Estos módulos pueden ser archivos locales compilados en formato CommonJS o servicios remotos a los que se accede por HTTP. Esto ofrece una base flexible para microservicios del lado del servidor, entrega dinámica de funcionalidades y recursos compartidos.

Consumer

Solo runtime

Si solo vas a consumir módulos en Node.js y no quieres introducir bundlers como Webpack/Rspack, puedes usar el enfoque de solo runtime. La idea clave es: no hacen falta plugins de build. Solo necesitas las APIs que proporciona @module-federation/runtime.

Pasos:

  1. Crea una instancia de MF con createInstance.
  2. Registra los remotes mediante el array remotes.
  3. Carga los módulos mediante loadRemote.

El siguiente ejemplo muestra cómo cargar un módulo remoto expuesto por HTTP:

import { createInstance } from '@module-federation/enhanced/runtime';

const mf = createInstance({
  name: 'node_host',
  remotes: [
    {
      name: 'node_remote',
      entry: 'http://localhost:3022/mf-manifest.json',
    },
  ],
});

async function handleHttpRequest(req: { query: any }, userId: string) {
  const remote = await mf.loadRemote('node_remote/api-handler');

  const result = await remote({
    method: req.method,
    path: req.path,
    query: req.query,
    body: req.body,
    user: { id: 'u_123' },
  });
  res.status(result.status).json(result.body);
}

Usar un plugin del bundler (Rspack/Webpack)

Si tu aplicación Node.js se compila con Webpack o Rspack, integrar Module Federation es sencillo: añade el plugin y la configuración de runtime necesaria.

Para el host (consumer), la clave es añadir @module-federation/node/runtimePlugin y configurar remoteType: 'script' y target: 'async-node', y después aplicar el resto de la configuración.

Ejemplo con Rspack (con Webpack es prácticamente igual):

rspack.config.js
const {
  ModuleFederationPlugin,
} = require('@module-federation/enhanced/rspack');

module.exports = {
  // ...
  target: 'async-node',
  plugins: [
    new ModuleFederationPlugin({
      name: 'node_host',
      runtimePlugins: [
        require.resolve('@module-federation/node/runtimePlugin'),
      ],
      remoteType: 'script',
      remotes: {
        node_remote: 'node_remote@http://localhost:3022/mf-manifest.json',
      },
    }),
  ],
};

Después de eso, puedes hacer import de módulos remotos directamente en tu código:

main.js
import node_remote_test from 'node_remote/test';

console.log(node_remote_test);

Provider

En el lado del producer, recomendamos usar Rslib. Solo tienes que usar el plugin @module-federation/rsbuild-plugin y configurar target: 'async-node' para generar un remote que se pueda consumir en Node.js.

Configuración clave de apps/node-remote/rslib.config.ts:

rslib.config.ts
import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
import { defineConfig } from '@rslib/core';
import mfConfig from './module-federation.config.ts';

export default defineConfig({
  plugins: [pluginModuleFederation(mfConfig, { target: 'node' })],
});

Si no usas Rslib, también puedes compilar el remote con Rspack/Webpack (la configuración es prácticamente la misma). Los puntos clave son: configurar target como async-node y generar remoteEntry.js con library.type = 'commonjs-module'.

Ejemplo con Rspack:

rspack.config.js
const {
  ModuleFederationPlugin,
} = require('@module-federation/enhanced/rspack');

module.exports = {
  target: 'async-node',
  plugins: [
    new ModuleFederationPlugin({
      name: 'node_remote',
      library: { type: 'commonjs-module', name: 'node_remote' },
      filename: 'remoteEntry.js',
      runtimePlugins: [
        require.resolve('@module-federation/node/runtimePlugin'),
      ],
      exposes: {
        './test': './src/expose.js',
      },
    }),
  ],
};

La configuración de Webpack es prácticamente igual que la del ejemplo de Rspack anterior.

Preguntas frecuentes

1. ¿Qué hace target: 'async-node'?

target: 'async-node' es un target de build de Webpack que genera una salida adecuada para Node.js con carga asíncrona. Es importante para el modelo de carga dinámica y asíncrona de Module Federation, sobre todo cuando necesitas await de nivel superior al cargar remotes.

2. ¿Por qué tengo que configurar remoteType: 'script'?

Actualmente, el runtime del bundler de MF solo admite el tipo de carga de remotes script, así que para consumir en Node.js tienes que configurar explícitamente remoteType: 'script'.

Referencias