@unily/angular-sdk
v19.2.3
Published
This SDK provides the tools needed to support Angular Module Federation development within Unily
Downloads
343
Readme
Unily - Angular SDK
This SDK provides the tools needed to support Angular Module Federation development within Unily. It includes a schematics package that automatically sets up the scaffolding for a new micro frontend module build, it generates the essential files and configurations needed to make Angular Module Federation facilities available within the set workspace.
Contents
- Prerequisites
- Installation
- Usage
- Running an MFE
- Deploying an MFE
- Deploy an MFE via a CI/CD pipeline
- Migrating an existing MFE
- Loading of Modules
- Lazy loading of a service / component from a remote module
Prerequisites
This project requires NodeJS and NPM to be installed. To make sure you have them available on your machine, try running the following commands:
npm -v
node -vInstallation
To run the schematics collection, first you'll need to install the following Angular development packages globally on your machine so they will provide you with the schematics command line tools:
npm install -g @angular-devkit/schematics
npm install -g @angular-devkit/schematics-cliNext, install this SDK globally on your machine:
npm install -g @unily/angular-sdkUsage
Target the workspace folder you wish to generate the micro frontend module in, then run the new-mfe schematic with the following command:
cd {path}
schematics @unily/angular-sdk:new-mfeThis will prompt you to input some configuration settings for the new project:
- Project name - This will set the workspace directory name accordingly
- Angular directive selector prefix - This will enforce a linting rule across the project requiring the set value as a prefix for the 'selector' property
- Webpack server port number - This will set the port number for the module to run on
After running the schematic, the workspace will be populated with the associated file structure. Package dependencies will also be downloaded and installed automatically to prepare the newly generated module for development.
Configuration
Environment configuration details can be fetched from the CMS.
Running an MFE
To start a module on your local environment, run the following command:
Note - This will serve the module on the configured port number.
npm run startDeploying an MFE
To deploy a module, run the following commands:
npm run build
npm run deployThis will prompt you to choose from different deployment options:
- Lazy Loaded - Choose whether the module should be lazily or eagerly loaded
- Starts enabled - Choose whether the module should be immediately enabled after being deployed
The module will then be added to the 'Application Federated Modules' page in the CMS once it's been successfully deployed.
Deploy via CI/CD pipelines
Alternative to deploying manually, deploying via CI/CD pipelines can be achieved by calling the deploy-ci script with
the required parameters.
To do so, update your pipeline to run the following command.
Note that the parameters passed to the npm script need to be passed to a node script hence the use of -- is very important
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecretThe deploy-ci script accepts the following options. Each option requires a value assigned to it. Options can be used
in the short or long version. Both will have the same result.
- -h, --host - The domain of your api gateway
- -c, --clientId - Your client ID. Contact customer support to provide you one.
- -s, --secret - Your secret key. Contact customer support to provide you one.
- -l, --lazy - Accepts a boolean value. True or false. Defaults to true if unspecified. If true, the module will be configured as lazy loaded. If false, it would be loaded eagerly.
- -e, --enabled - Accepts a boolean value. True or false. Defaults to true if unspecified. If true, the module will be enabled, otherwise it will not and will never be loaded.
Examples
- Deploying a module that is enabled, and eagerly loaded
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret -l false- Deploying a module that is lazy loaded but not enabled
npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret -e false- Deploying a module that is eagerly loaded but not enabled
npm run deploy-ci -- --host https://myexamplehost-api.unily.com --clientId myClientId --secret myClientSecret --lazy false --enabled falsePowershell and Node 22
Users working with Powershell and Node 22 might experience an issue when running the deploy-ci script as below. This issue seems to be present only in Node 22+ & Powershell.
PS C:\path\to-my-repo> npm run deploy-ci -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecret
Run arbitrary package scripts
Usage:
npm run-script <command> [-- <args>]
Options:
[-w|--workspace <workspace-name> [-w|--workspace <workspace-name> ...]]
[-ws|--workspaces] [--include-workspace-root] [--if-present] [--ignore-scripts]
[--foreground-scripts] [--script-shell <script-shell>]
aliases: run, rum, urn
Run "npm help run-script" for more infoIn such a situation, you should be able to run the command by using -- -- instead of -- to pass on the parameters
npm run deploy-ci -- -- -h https://myexamplehost-api.unily.com -c myClientId -s myClientSecretMigrating an existing MFE
Starting with v19.2.1, this package ships migration schematics to migrate your existing MFEs to the latest changes. To run a migration schematic run the following command
schematics @unily/angular-sdk:migrate@<version>The current migration versions available are
- 18.1.5
- 19
- 19.1
To have a smooth experience, each migration is expected to run on a version of the repository prior to the one being executed. For example, to run the migration for v18.1.5, it is advised that the repository contains the changes from v18.1.4. To run migration for v19, it is advised to have the changes done in v18.1.5, and so on and so forth.
A migration can be partially run if the necessary criteria for a migration are not met.
Example
To migrate an MFE to v19 of the SDK run the following command
schematics @unily/angular-sdk:migrate@19Module loading
These are the capabilities of loading a remote module:
- Can be side-loaded through CMS config ✔
- Can be imported ✔
- Can be lazy loaded ✔
- Can be deployed to a blob ✔
- Requires environment redeployment on change ❌
- Can side-load modules through CMS config ❌
Note: Remote modules do not import side-loaded modules explicitly, but inherit any side-loaded module configurations. When running stand-alone, the side-loaded modules will not be available to the remote module.
Dynamically importing modules
A remote module can import myFederatedModules, applicationFederatedModules and unilyFederatedModules modules. Each remote module is configured separately and can import other modules' type definitions when their name is added inside module-federation/mf.config.ts file:
mf.config.ts
export const mfConfig = {
projectName: 'my-first-mfe',
dependencies: {
// Load types for MFEs which have been deployed to the Client's Unily instance
applicationFederatedModules: [
'my-second-mfe'
],
// Load types from MFEs available to all Unily instances
unilyFederatedModules: [
/*
'unily-ui-library'
...
*/
],
// Load types AND remote modules from the local environment.
// This is useful for testing and development without having to side-load modules and configure them in the Unily platform.
myFederatedModules: [
/*
'my-local-mfe',
*/
]
},
/**
* Authentication token is needed to load types for `applicationFederatedModules` and `unilyFederatedModules`
* OR to deploy the current module.
*
* Fetch your endpoint and auth token from
* 'My Federated Modules' or 'Application Federated Modules' pages in CMS
* by clicking on 'Get PAT Token' and copying the values provided
* in the overlay.
*/
endpoint: '',
authToken: '',
/**
* Optional proxy configuration for environments behind corporate firewalls.
* Uncomment and configure if needed.
*/
// proxy: {
// host: 'your-proxy-host.com',
// port: 8080,
// auth: {
// username: 'proxy-username',
// password: 'proxy-password'
// }
// }
};Proxy configuration
If you're working behind a corporate firewall or proxy server, you can configure proxy settings in mf.config.ts file:
mf.config.ts
export const mfConfig = {
// ... other configurations
proxy: {
// The hostname or IP address of your corporate proxy server
host: 'your-proxy-host.com',
// The port number that your proxy server is listening on
port: 8080,
// Authentication credentials for proxy servers that require login
auth: {
// Your proxy server username
username: 'proxy-username',
// Your proxy server password
password: 'proxy-password'
}
}
};The proxy configuration will be automatically applied to all HTTP requests made during module federation operations, including manifest fetching and module deployment.
Lazy loading a Service and Component from a remote module in Angular
Lazy loading defers the loading of a module until it is actually required. This is ideal for optimizing performance, especially in large applications where not all functionality is needed up front. Angular supports lazy loading via dynamic imports, which enable you to load and access services or components from a remote module only when they are first used.
The example below demonstrates how to configure a remote module to expose both a component (standalone or not) and a service, enabling them to be lazy loaded by a consuming MFE.
Note: Components which should not be publicly available for consumption should not be added to the exports array as noted below.
Step-by-Step example: Exposing Services and Components via public.export.ts
In your MFE, use the public.module.metadata.ts file to declare what should be exposed for lazy loading.
Key Points
- Standalone Components (Angular 16+): Should be added to both
importsandexportsarrays. - Traditional (non Standalone components) Should be added to both the
declerationsandexportsarrays. - Services: Should be listed in the
providersarray, ideally using a string token to decouple the service from direct class references, making the service available to be retrieved via thegetService(...)function defined within thepublic.module.tsfile.
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HelloWorldComponent } from '../app/hello-world/hello-world.component';
import { MyComponent } from '../app/my/my.component';
import { LoggingService } from '../app/core/services/logging.service';
import PublicModule from './@internal/public.module';
export const metadata = {
imports: [
BrowserModule,
MyComponent
],
declarations: [
HelloWorldComponent
],
exports: [
// Components made available to remote consumers.
HelloWorldComponent,
MyComponent
],
providers: [
// Services exposed to remote modules.
{
provide: 'LoggingService',
useClass: LoggingService
}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
} as NgModule;afterBootstrap
Use the afterBootstrap function to execute logic immediately after the module is loaded.
export const afterBootstrap = async (instance: PublicModule) => {
const logService = instance.getService('LoggingService') as LoggingService;
logService.log('Hello world example has been bootstrapped!');
};Webpack Configuration for Exposing the Public Folder
To enable lazy loading of components and services from a remote Angular Micro Frontend (MFE), the Public folder is explicitly exposed via the webpack.config.js file. This is essential for making the module accessible to host or shell applications using Module Federation.
Here’s a sample configuration that illustrates how to expose the Public module:
const { shareAll, withModuleFederationPlugin } = require('@angular-architects/module-federation/webpack');
const { FederatedTypesPlugin } = require('@unily/module-federation-typescript');
const { SasPlugin } = require('@unily/sas-plugin');
const path = require('path');
const { getAngularVersion } = require('@unily/utils');
const remoteData = require('./module-federation/remotes');
const remotes = {};
for (const remote of remoteData) {
remotes[remote.name] = remote.url;
}
const sharedMappings = shareAll({ requiredVersion: 'auto' });
delete sharedMappings['@unily/remote-service'];
sharedMappings['@angular/core'].requiredVersion = getAngularVersion();
sharedMappings['@angular/platform-browser'].requiredVersion = getAngularVersion();
const federationConfig = {
name: 'my-first-mfe',
filename: 'remoteEntry.js',
remotes,
exposes: {
'./Public': './src/exports/@internal/public.exports.ts'
},
shared: {
// Ensures shared packages are reused and compatible across MFEs
...sharedMappings
}
};
module.exports = withModuleFederationPlugin(federationConfig);
module.exports.plugins.push(
new FederatedTypesPlugin({ federationConfig }),
new SasPlugin()
);
module.exports.experiments.topLevelAwait = true;
module.exports.devServer = {
static: {
directory: path.join(__dirname, 'dist')
}
};
module.exports.target = 'web';By exposing ./Public, all components and services declared in that module become available to be dynamically imported by host applications.
Consuming other MFEs with importRemote
To lazy load other MFEs and consume its services and components, use importRemote from @unily/remote-service npm library. When invoked, it loads the remote module at runtime, makes services accessible via getService(...), and making any component that is exported usable immediately in the DOM (when the CUSTOM_ELEMENTS_SCHEMA schema is used).
In the example below, we have a component in an MFE (my-first-mfe) retrieve and consuming a service (LoggingService) declared in a second MFE (my-second-mfe).
Note: The import path for the typings depend heavily on where the service is placed, and will differ from a real scenario.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { importRemote, RemoteNgModule } from '@unily/remote-service';
import { LoggingService } from '@mf-types/my-second-mfe/types';
@Component({
selector: 'my-component',
standalone: true,
template: '<button (click)="loadLoggingService()">Load Remote Module</button>',
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class MyComponent {
loggingService?: LoggingService;
async loadLoggingService() {
this.loggingService = await this.getLoggingService();
console.log('LoggingService initialized:', this.loggingService);
}
private async getLoggingService(): Promise<LoggingService> {
if (this.loggingService) {
return this.loggingService;
}
// Lazy load the remote module. Services can be fetched via getService, and components become immediately usable in templates.
const myMFE = await importRemote<RemoteNgModule>('my-second-mfe');
return myMFE.getService('LoggingService');
}
}Key Notes for Consuming a Remote Module
Remote Declaration
The remote module (e.g.'my-second-mfe') should be declared in yourmodule-federation/mf.config.tsfile under thedependenciesobject. This ensures correct resolution at runtime and enables accurate type inference for services and components.In order to be consumed, it must also be configured accordingly within the Unily platform.
Accessing Remote Services
CallinggetService('LoggingService')returns the instance of the service exposed in the remote’spublic.exports.tsfile within themy-second-mfeMFE. This allows you to consume remote business logic just like a local Angular service.Rendering Remote Components
Remote components can be rendered dynamically in templates as custom elements. This is made possible by includingCUSTOM_ELEMENTS_SCHEMAin your component’s schema declaration.
