Skip to content

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