This repository contains the Discovery API and Asset Store configuration for an on-premises Microfrontends setup.
- Discovery API (Node.js/Express): Manages the
federation.manifest.jsondynamically. - Asset Store (Nginx): Serves static artifacts with proper CORS and caching headers.
The Discovery API is a lightweight Express server written in TypeScript that stores the manifest in a JSON file. It uses Pino for high-performance logging.
Installation:
npm installRunning:
# To compile the TypeScript code:
npm run build
# To run the compiled code:
npm start
# For development (direct execution with ts-node and pretty logs):
npm run devEnvironment Variables:
PORT: Port to run the server on (default: 3000).MANIFEST_PATH: Path to the manifest JSON file.REGISTRATION_SECRET: Secret key for the/api/registerendpoint.
For easier configuration, copy .env.example to .env and adjust the values:
cp .env.example .envThe Nginx configuration is located in nginx/nginx.conf. It should be used to serve the directory where your MFE artifacts are stored.
Key features:
- CORS: Explicitly enabled for all origins with full
OPTIONSpreflight handling (required for Native Federation). - Performance: Gzip compression enabled for all JS, CSS, and JSON assets.
- Smart Caching:
remoteEntry.jsonandindex.htmlare never cached (no-store, no-cache) to ensure the Shell always gets the latest version.- Versioned artifacts (e.g., in
/v1.0.0/) are served with Immutable cache headers for maximum performance.
- Security: Includes
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN, andX-XSS-Protectionheaders.
You can run both the Discovery API and the Asset Store together using Docker Compose. This setup uses volumes to persist data and serve assets from your local machine.
Prerequisites:
- Docker and Docker Compose installed.
Steps:
-
Start the environment: Copy the example environment file and adjust if necessary:
cp .env.example .env
The
.envfile contains:REGISTRATION_SECRET: Your secret for the API.DATA_PATH: The host path where your assets are stored (defaults to./data).SHELL_PATH: (Optional) A specific host path for the Shell assets. Defaults to${DATA_PATH}/shell.
Then run:
docker compose up --build
-
Access the services:
- Unified Entry Point:
http://localhost(orhttp://mfe-assets.localif configured in hosts) - Manifest API:
http://localhost/api/manifest - Assets:
http://localhost/<mfe-name>/<version>/...
- Unified Entry Point:
How it works:
- Nginx as Reverse Proxy: Nginx handles all incoming traffic on port 80. Requests starting with
/api/are proxied to thediscovery-server. All other requests are served as static assets. - Directory Structure:
data/manifest/: Stores themanifest.json(the registry).data/mfe/: Stores the MFE artifacts (the storage).data/shell/: Stores the Shell application assets (e.g.,index.html).
- Shared Storage:
- The
discovery-servermaps${DATA_PATH}(default./data) to/app/datato manage the manifest. - The
asset-servermaps subfolders of${DATA_PATH}to/var/www/mfe-storageand/var/www/shell.
- The
- When you register an MFE, the
manifest.jsonis updated in your data folder. - Any artifacts you place in your data folder under
mfe/<mfe-name>/<version>/will be immediately served by theasset-serverathttp://localhost/<mfe-name>/<version>/.
Update your main.ts to fetch the manifest from the Discovery API:
import { initFederation } from '@angular-architects/native-federation';
// Point this to your Discovery API
const DISCOVERY_URL = 'http://mfe-assets.local/api/manifest';
fetch(DISCOVERY_URL)
.then(res => res.json())
.then(manifest => initFederation(manifest))
.catch(err => {
console.error('Failed to load manifest, falling back to local defaults', err);
// Fallback: Use a local hardcoded manifest if the service is down
return initFederation('federation.manifest.json');
})
.then(_ => import('./bootstrap'))
.catch(err => console.error(err));Example script for your remote MFE repositories:
#!/bin/bash
# 1. Variables
VERSION=$(node -p "require('./package.json').version")
MFE_NAME="myMicrofrontend"
STORAGE_PATH="/var/www/mfe-storage/$MFE_NAME/v$VERSION"
ASSETS_HOST="http://mfe-assets.local"
DISCOVERY_API="http://mfe-assets.local/api"
SECRET="your-secure-secret-here"
# 2. Build the project
npm run build
# 3. Copy files to the Nginx storage (via SSH/Rsync/Volume Mount)
# Ensure the directory exists
ssh user@mfe-assets.local "mkdir -p $STORAGE_PATH"
# Upload artifacts
scp -r ./dist/my-app/* user@mfe-assets.local:$STORAGE_PATH
# 4. Register the new version in the Discovery API
curl -X POST "$DISCOVERY_API/register" \
-H "Authorization: Bearer $SECRET" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"$MFE_NAME\",
\"url\": \"$ASSETS_HOST/$MFE_NAME/v$VERSION/remoteEntry.json\"
}"The Shell (or Host) application is treated as the default application. Nginx is configured to serve the Shell from /var/www/shell when a request does not match any MFE assets or API routes.
Deployment:
- Build your Shell application.
- Upload the artifacts (including
index.html) to thedata/shell/directory on the server (or the path specified bySHELL_PATH). - Nginx will automatically serve the Shell's
index.htmlfor the root URL (/) and handle SPA routing by falling back toindex.htmlif a file is not found.
The repository comes with an example MFE to help you verify your setup:
- Example Manifest: Located at
data/manifest/manifest.json. - Example MFE Artifact: A dummy
remoteEntry.jsonatdata/mfe/example-app/v1.0.0/remoteEntry.json. - Example Shell Artifact: A dummy
index.htmlatdata/shell/index.html.
To test via browser or curl:
- Get Shell:
curl http://localhost/(should return the Shell index.html) - SPA Fallback:
curl http://localhost/some-route(should also return the Shell index.html) - Get manifest:
curl http://localhost/api/manifest - Get MFE asset:
curl http://localhost/example-app/v1.0.0/remoteEntry.json