Getting Started
The first step is to create a Description of your new (or existing) Microfrontend. The Description acts as a contract between the Host Application and the Microfrontends Server. A definition can be written as YAML or JSON and can contain multiple Microfrontends.
Things you have to take into consideration for the Description:
- What are my initial assets and which module system do/will I use?
- Does my Microfrontend require proxying of APIs and assets (because it is not publicly accessible or the APIs require security)
- Does my Microfrontend require a configuration at startup?
- Will my Microfrontend send or receive messages from other Microfrontends on a page?
- Does my Microfrontend has functionality that depend on the users permissions?
- Does/should my Microfrontend provide Server-Side Rendering (SSR)?
Description Example
$schema: 'https://open-microfrontends.org/schemas/1-0-0.json'
openMicrofrontends: 1.0.0
servers:
- url: 'http://localhost:7890'
description: Local Test Server
microfrontends:
- name: My First Microfrontend
assets:
basePath: /public
js:
moduleSystem: ESM
initial:
- Microfrontend.js
rendererFunctionName: startMyFirstMicrofrontend
config:
schema:
type: object
properties:
welcomeMessage:
type: string
required: ["welcomeMessage"]
default:
welcomeMessage: Hello World!
messages:
ping:
publish: true
subscribe: true
schema:
type: object
properties:
ping:
const: true
required: ["ping"]
The next step is to generate type-safe code from the Description. You can use the OpenMicrofrontends Generator or any other OpenMicrofrontends generator.
The following needs to be generated:
- A Renderer interface on the Microfrontend side
- A Starter and (if necessary) Host Backend Integrations on the Host Application
Renderer
Here is an example Renderer implementation based on an interface generated by OpenMicrofrontends Generator:
import {
MyFirstMicrofrontendRenderer,
MyFirstMicrofrontendRendererFunctionName,
} from './_generated/microfrontendsRenderers';
const renderer: MyFirstMicrofrontendRenderer = async (host, context) => {
const {config, messageBus} = context;
host.innerHTML = `
<div>
<h2>${config.welcomeMessage}</h2>
</div>
`;
const onPing = () => { /* do something */ };
// Type safe!
messageBus.subscribe('ping', onPing);
return {
onRemove: () => {
host.innerHTML = '';
messageBus.unsubscribe('ping', onPing);
}
}
}
// If you bundle your code to ESM oder SystemJS
export default {
[MyFirstMicrofrontendRendererFunctionName]: renderFn,
};
// Or otherwise (this always works)
// window[MyFirstMicrofrontendRendererFunctionName] = renderFn;
All you need now is to put the code above into your index file, bundle it and add a server that provides it at /public/Microfrontend.js (because the basePath is /public).
Starter
On the Host Application side, you can start the Microfrontend like this with a Starter generated by OpenMicrofrontends Generator:
import {startMyFirstMicrofrontend} from './_generated/microfrontendStarters';
const hostElement = document.getElementById('root');
const {close, messages} = await startMyFirstMicrofrontend(
'https://my-microfrontend-server.com', hostElement, {
id: '1',
// lang: 'en',
// user,
config: {
welcomeMessage: 'Microfrontend Demo!',
},
messageBus: globalMessageBus,
});
// Send a message to the Microfrontend - type-safe!
messages.publish('ping', { ping: true });
Host Backend Integration
The example Description above does not contain any definitions that require a Host Backend Integration. Here is a more complex example with API proxies, security and User Permissions:
$schema: 'https://open-microfrontends.org/schemas/1-0-0.json'
openMicrofrontends: 1.0.0
servers:
- url: 'http://localhost:8080'
description: Local Test Server
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: X-API-KEY
microfrontends:
- name: My Second Microfrontend
assets:
buildManifestPath: /package.json
js:
moduleSystem: SystemJS
initial:
- Microfrontend.js
rendererFunctionName: startMySecondMicrofrontend
userPermissions:
provided:
path: /permissions
security:
- ApiKeyAuth: [ ]
permissions:
- name: deleteCustomer
description: The user has the permission to delete a customer
apiProxies:
bff:
path: /api
security:
- ApiKeyAuth: [ ]
someExternalApi:
targets:
- url: "http://my-service.my-dev-namespace.svc.cluster.local:8080"
description: My Service DEV
config:
schema:
type: object
properties:
customerId:
type: string
description: The customer ID
required:
- customerId
default:
customerId: '1000'
OpenMicrofrontends Generator can generate the necessary Host Backend Integration code, which can be used like below (here, for example, with Express).
First, define the config:
import {MySecondMicrofrontendBaseSetup} from './_generated/microfrontendHostIntegrations';
export default class MySecondMicrofrontendBaseSetupImpl implements MySecondMicrofrontendBaseSetup {
get microfrontendBaseUrl() {
return 'http://second-microfrontend.my-test-namespace.svc.cluster.local:8080';
};
async getUser(req: IncomingMessage) {
// TODO
return null;
}
async apiProxyRequestBffGetSecurityHeaders(req: IncomingMessage): Promise<Record<string, string>> {
return {
'x-api-key': '123456',
};
}
get apiProxySomeExternalApiUrl() {
return 'http://my-service.my-test-namespace.svc.cluster.local:8080';
};
async userPermissionsRequestGetSecurityHeaders(req: IncomingMessage): Promise<Record<string, string>> {
return {
'x-api-key': '123456',
};
}
}
Then, add the generated middleware:
import {mySecondMicrofrontendHostIntegrationMiddleware} from './_generated/microfrontendHostIntegrations';
const app = express();
// ...
app.use(mySecondMicrofrontendHostIntegrationMiddleware(
new MySecondMicrofrontendBaseSetupImpl()
));
In the Microfrontend the proxies and the User Permissions can be used like this:
const renderer: MySecondMicrofrontendRenderer = async (host, context) => {
const {config, apiProxyPaths, permissions} = context;
// type: boolean
const userHasPermissionToDeleteCustomer = permissions.deleteCustomer;
// Load customer (via proxy)
const response = await fetch(`${apiProxyPaths.bff}/customers/${config.customerId}`);
}
The Starter changes a bit: You don't have to pass the URL anymore and can no longer set user or permissions:
const {close, messages} = await startMyFirstMicrofrontend(
hostElement, {
id: '1',
config: {
welcomeMessage: 'Microfrontend Demo!',
},
messageBus: globalMessageBus,
});
OpenMicrofrontend Generator Documentation
Example Microfrontends and Host Integrations