This is the mfe-core single-spa application which loads all other Topcoder micro-frontend (MFE) applications. It always loads the Topcoder Header Microapp which provides the top-level navigation, handles authorization, and loads other microapps depending on the current URL.
Following are the list of sections defined in this document,
- Overview
- Requirements
- Local Development Setup
- Git
- Application Configuration
- NPM Commands
- Local Deployment
- Deployment to Development
- Deployment to Production
- Segment Analytics
- Add or Remove child app
- Add-hoc child app replacement (import override)
- Creating child apps (microapps)
Topcoder Single Spa consist of 3 main components:
- This frame application,
mfe-core
, which is a single-spa application. The only function of this application is to register other micro applications to load. - Topcoder Header Microapp - micro application which is always loaded by the frame application and shows the top-level navigation bar and handles user authorization.
- Any other micro application can be loaded as main content of the overall application.
Use the node version manager nvm to easily and safely manage the required version of NodeJS (aka, node). Download and install it per the instructions for your development operating system. Installing a version of node via nvm
will also install npm
.
Once nvm is installed, run:
$ nvm install <insert node version>
Note: the node version required at the time of this writing is
10.22.1
At the root of the project directory you'll notice a file called .nvmrc
which specifies the node version used by the project. The command nvm use
will use the version specified in the file if no version is supplied on the command line.
See the nvm Github README for more information on setting this up.
You can verify the versions of nvm
, node
, and npm
using the commands below.
Command | Version |
---|---|
$ npm -v |
6.14.12 |
$ node -v |
v10.22.1 |
$ nvm --version |
0.39.1 |
$ nvm current |
v10.22.1 |
Use the VS Code IDE for MFE development.
You will need to add the following line to your hosts file. The hosts file is normally located at /etc/hosts
(Mac). Do not overwrite the existing localhost entry also pointing to 127.0.0.1
127.0.0.1 local.topcoder-dev.com
The MFE can run in a non-ssl environment, but auth0 will complain and throw errors. In order to bypass this, you will need to install local-ssl-proxy to run the site in ssl. The following command will install it globally:
$ npm i -g local-ssl-proxy
The MFE Core Frame needs to run separate local server and client processes, each one in a separate terminal session. The navbar also needs to run its server in a terminal, along with the local-ssl-proxy
server that will allow you to use https endpoints locally. Finally, each of the other micro front-end applications you want to run will also each run in their own terminal session.
When developing one of the micro front-end applications you will therefore have 5 terminal sessions running at the same time:
mfe-core
servermfe-core
clientmfe-header
applicationlocal-ssl-proxy
server- the MFE app you're developing
Given this complexity, it is recommended that you use a tool like iTerm2 (on Mac) or an equivalent terminal shell on Windows to make terminal management simpler. iTerm2 allows you to setup a pre-defined window layout of terminal sessions, including the directory in which the session starts. This setup, along with simple shell scripts in each project that configure and start the environment, will allow you to get your development environment up and running quickly and easily.
We use linting to enforce standardization. Please make sure all lint rules pass before creating PRs.
Use the following command to check for lint errors:
$ npm run lint
When working on Jira tickets, we link associated Git PRs and branches to the tickets. Use the following naming convention for branches:
[TICKET #]_short-description
e.g.: PROD-1516_work-issue
We use Smart Commits to link comments and time tracking to tickets. You would enter the following as your commit message:
[TICKET #] #comment <commit message> #time <jira-formatted time>
e.g.: PLAT-001 #comment adding readme notes #time 45m
This mfe-core
app has 2 types of configs:
-
Import mapping for the frame, containg
micro app name
andrelative url path
for each micro app. The configuration files are available on TC AWS S3 and have public access.i. What needs to be added for a new micro-app:
{ "imports": { "@topcoder/mfe-header": "https://mfe.topcoder-dev.com/navbar/topcoder-mfe-header.js", "<MICRO_APP_NAME>": "<RELATIVE_URL_PATH>" } }
ii. Location of the AWS S3 files:
- Configure micro app names and relative URL to be used when deployed to production environment in file at location :
https://tc-platform-prod.s3.amazonaws.com/micro-frontends/micro-frontends-config-production.json
- Configure micro app names and relative URL to be used when deployed to development environment in file at location :
https://tc-platform-dev.s3.amazonaws.com/micro-frontends/micro-frontends-config-development.json
- Configure micro app names and relative URL to be used when deployed to local environment in file at location :
./mfe-core/config/micro-frontends-config-local.json
- Configure micro app names and relative URL to be used when deployed to production environment in file at location :
-
Route mapping handled by the frame, containing
route path
andmicro app name
for each micro app. The configuration files are available on TC AWS S3 and have public access.i. What needs to be added for a new micro-app:
<route path="<RELATIVE_URL_PATH>"> <application name="<MICRO_APP_NAME>"></application> </route>
ii. Location of the AWS S3 files:
- Configure route path and micro app name to be used when deployed to production environment in file at location :
https://tc-platform-prod.s3.amazonaws.com/micro-frontends/micro-frontends-routes-production.txt
- Configure route path and micro app name to be used when deployed to development environment in file at location :
https://tc-platform-dev.s3.amazonaws.com/micro-frontends/micro-frontends-routes-development.txt
- Configure route path and micro app name to be used when deployed to development environment in file at location :
./mfe-core/config/micro-frontends-routes-local.txt
- Configure route path and micro app name to be used when deployed to production environment in file at location :
Command | Description |
---|---|
npm start |
Run server which serves production ready build from dist folder |
npm run start-server |
Run server locally for local development (calls on local-server npm script) |
npm run start-client |
Run client locally for local development (calls on local-client npm script) |
npm run build |
Build app with webpack and puts files to the dist folder |
npm run local-server |
Run the server on local machine with nodemon |
npm run local-client |
Run the frontend on local machine with webpack-dev-server |
npm run lint |
Check code for lint errors |
npm run format |
Format code using prettier |
npm run test |
Run unit tests |
Local Deployment from multi web servers (nodemon & webpack-dev-server) for local development - (most common)
To run the mfe-core
app locally, run the following commands from the project root ./mfe-core
:
Terminal 1
$ npm run start-server
Terminal 2
$ npm run start-client
To deploy mfe-core
app locally run inside the project root ./mfe-core
:
Command | Description |
---|---|
$ export APPMODE="development"; export APPENV="local" |
to add environment variables for application building on local |
$ nvm use 10.22.1 |
configure the required node and npm versions via nvm |
$ npm i |
to install dependencies |
$ npm run build |
to build the application and store in dist/ |
$ npm start |
to start the app on port 80 , served from dist/ |
http://local.topcoder-dev.com:3000/micro-frontends-react-route |
open url in browser to access the micro frame with react micro app and micro navbar app |
local.topcoder-dev.com
with port 3000
. On your local machine access file /etc/hosts
add the line 127.0.0.1 local.topcoder-dev.com
and open app by URL http://local.topcoder-dev.com:3000
Command | Description |
---|---|
$ export APPMODE="development"; export APPENV="dev"; export PORT=80 |
to add environment variables for application building on development |
$ nvm use 10.22.1 |
to configure the required node and npm versions via nvm |
$ npm i |
to install dependencies |
$ npm run build |
to build the application and store in dist/ |
$ npm start |
to start the app on port 80 , served from dist/ |
https://mfe.topcoder-dev.com/micro-frontends-react-route |
open url in browser to access the micro frame with react micro app and micro navbar app |
Command | Description |
---|---|
$ export APPMODE="production"; export APPENV="prod"; export PORT=80 |
to add environment variables for application building on production |
$ nvm use 10.22.1 |
to configure the required node and npm versions via nvm |
$ npm i |
to install dependencies |
$ npm run build |
to build the application and store in dist/ |
$ npm start |
to start the app on port 80 , served from dist/ |
https://mfe.topcoder.com/micro-frontends-react-route |
open url in browser to access the micro frame with react micro app and micro navbar app |
Make sure you have Heroky CLI installed and you have a Heroku account. And then inside the project folder run the next commands:
- If there is not Git repository inited yet, create a repo and commit all the files:
git init
git add .
git commit -m'inital commit'
heroku apps:create
- create Heroku appgit push heroku master
- push changes to Heroku and trigger deploying⚠️ NOTE : Authorization would not work because only predefined list of domain allowed byaccounts-app
.
Because analytics can be normally initialized once per website, we are initializing Segment Analytics inside the Frame app instead of each child app separately. See Segment Analytics Quick Guide.
- Analytics requires
SEGMENT_ANALYTICS_KEY
to work. - It should be set as environment variable
SEGMENT_ANALYTICS_KEY
during runningnpm run build
(for production build) ornpm run local-client
(for local development). - If
SEGMENT_ANALYTICS_KEY
environment variable is not set during the build process, the Segment Analytics would not be initialized. - Analytics would be exposed to the
window
object aswindow.analytics
. See Segment Analytics API Guide. - Note, that because we can build the Frame app without analytics initialized, all the child apps which use analytics should always check if it's initialized before usage, for example like this:
if (window.analytics && typeof window.analytics.page === "function") { window.analytics.page(); }
- Each child app should take care about calling
window.analytics.page()
each time the page is changed. We can pass additional arguments to this method if needed. - We can enable debug mode by calling
analytics.debug();
inside browser console, see debug documentation. - TODO: we might consider implementing one global logic for calling
window.analytics.page()
inside Frame of Navbar app so child apps wouldn't worry about this. Though we have to make sure that it works smoothly. In particular we have to make sure that if child app updates page<title>
thenwindow.analytics.page()
is called after that and logs to the analytic correct page title. Also, child apps might want to provide additional arguments when callingwindow.analytics.page()
. So we might come to this improvement in some time, after we try the current approach.
For adding a child app to the root app make the following steps:
- Add child app path to importmap. File underpath
-
https://tc-platform-prod.s3.amazonaws.com/micro-frontends/micro-frontends-config-production.json
for production deployment -
https://tc-platform-dev.s3.amazonaws.com/micro-frontends/micro-frontends-config-development.json
for development deployment -
./mfe-core/config/micro-frontends-config-local.json
for local deploymentReact example:
"@topcoder/micro-frontends-react-app": "//localhost:8500/topcoder-micro-frontends-react-app.js"
Angular example:
"@topcoder/micro-frontends-angular-app": "//localhost:4200/topcoder-micro-frontends-angular-app.js"
- Add a route which should show the app. File underpath
-
https://tc-platform-prod.s3.amazonaws.com/micro-frontends/micro-frontends-routes-production.txt
for production deployment -
https://tc-platform-dev.s3.amazonaws.com/micro-frontends/micro-frontends-routes-development.txt
for development deployment -
./mfe-core/config/micro-frontends-routes-local.txt
for local deployment<route path="<RELATIVE_URL_PATH>"> <application name="<MICRO_APP_NAME>"></application> </route>
To run a child app locally we always need to have frame (mfe-core
) which would load a child app. But the cool thing is that we don't have to deploy the frame locally and we can use already deployed frame app. We can use a dev tool to override a child app URL so it would be loaded from the local machine by following the next steps:
- Load already deployed frame app in the browser.
- Open browser console and set
devtools
flag in the local storage by executing the next command:localStorage.setItem("devtools", true);
- Reload the page, you would see the
{...}
icon in the bottom right corner. - Clicking it would open Import Map Overrides tool where you can change the URL for any loaded microapp to the local URL.
⚠️ NOTE : that if the frame is deployed usingHTTPS
protocol, then you have to run microapp locally using HTTPS protocol too. For example this could be done bynpm run dev-https
command in the Topcoder Navbar Microapp. Also, you would have to open the local app using direct HTTPS link first to make sure that browser allows loading it. - Now reload the page and you would see the microapp loaded into the frame from the local machine. You can update it locally and see changes in the frame which is deployed to the remote server.
See video Javascript tutorial: local development with microfrontends, single-spa, and import maps as a part of the official documentation of Single Spa.
f that once we configure React/Angular application be run as child application in Single SPA we cannot run it as standalone application anymore. So while this app can be deployed and run independently, we would need some frame single-spa which would load it. While technically we can achieve running this app as standalone app it's strongly not recommended by the author of the single-spa
approch, see this GitHub Issue for details.
Any existent Angular app could be configured to be used as child app, but exact way would depend on the Angular version, see documentation for details. For the Angular >= 7 the next approach should work.
-
If you don't have existent Angular app, create a new app using Angular CLI:
ng new micro-frontends-angular-app --routing --prefix tc-ex
⚠️ NOTE : that the--prefix
is important so that when you have multiple angular applications their component selectors won't have the same names. -
Now as we have Angular app, in the root of the application run the following command which would adapt Angular app so it could be run as a child app:
ng add single-spa-angular
Answer the following questions:
- Does your application use Angular routing?:
Y
(we've created the app using--routing
flag, so yes) - Does your application use BrowserAnimationsModule?
N
(using browser animations might break re-rendering of the app and might require additional fixes)
- Does your application use Angular routing?:
-
Run
npm i
to install dependencies added byng add single-spa-angular
-
Now you must configure routes. Update
src/app/app-routing.module.ts
:-
Add 2 imports:
import { EmptyRouteComponent } from "./empty-route/empty-route.component"; import { APP_BASE_HREF } from "@angular/common";
-
Add route to the
routes
array:const routes: Routes = [ // ... other routes here { path: "**", component: EmptyRouteComponent }, // add this line ];
-
Add provider to the
providers
array:@NgModule({ imports: [RouterModule.forRoot(routes)], // imports stay as it is exports: [RouterModule], // exports stay as it is providers: [ // add providers array if it's not here // ... other providers here { provide: APP_BASE_HREF, useValue: '/' }, // add this line ], })
-
Add a declaration for
EmptyRouteComponent
inapp.module.ts
:// add import for `EmptyRouteComponent` import { EmptyRouteComponent } from './empty-route/empty-route.component'; ... declarations: [ // ... other declarations here EmptyRouteComponent, // add this line ],
-
Now the new Angular app is ready to be run as child app. You can run it by npm run serve:single-spa:micro-frontends-angular-app
. To see it in browser you have to config the root app to show it first, see "Add/Remove child app" section.
The URL for the Angular app started this way would look like:
//localhost:4200/micro-frontends-angular-app.js
npm start
would not work.
The easiest way to create a new React child app is using create-single-spa CLI tool.
Run npx create-single-spa
and answer questions:
- Directory for new project:
micro-frontends-react-app
- Select type to generate:
single-spa application / parcel
- Which framework do you want to use?:
react
- Which package manager do you want to use?:
npm
- Will this project use Typescript?:
N
- Organization name:
topcoder
- Project name:
micro-frontends-react-app
Now the new React app is created in the folder micro-frontends-react-app
. You can run it by npm start -- --port 8500
. To see it in browser you have to config the root app to show it first, see "Add/Remove child app" section.
The URL for the React app started this way would look like:
//localhost:8500/topcoder-micro-frontends-react-app.js
There is no universal approach to run any React app as child app in Single SPA. This is because unlike Angular application which always use Angular CLI, each React application has it's own Webpack config. And to be able to run React app as a child microapp we need the Webpack to be configured in a certain way.
- Here is Official Video form the creator of Single Spa on How To Convert a create-react-app (CRA) project to single-spa
- git clone https://github.com/topcoder-platform/mfe-core.git
- git clone https://github.com/topcoder-platform/mfe-header.git
- git clone https://github.com/topcoder-platform/micro-frontends-react-app.git
- git clone https://github.com/topcoder-platform/micro-frontends-angular-app.git and create a folder (ex: 'auth0-local-login'), and save the following file: 'https://accounts-auth0.topcoder-dev.com/setupAuth0WithRedirect.js' into that folder. After 'setupAuth0WithRedirect.js' file was saved, create an empty 'index.html' file with the following content:
<!DOCTYPE html>
<html>
<head>
<title>Auth0</title>
<meta charset="utf-8" />
<script language="javascript" type="text/javascript" src="./setupAuth0WithRedirect.js"></script>
</head>
<body>
Loaded...redirecting to auth0.(see browser console log)
<script>
window.onload = authSetup;
</script>
<a href="?retUrl=http://localhost:3000" >Login</a>
</body>
</html>
(1). root-config: open Terminal #1 change the current dir to the root-config folder and apply patch:
- cd mfe-core
- git apply ../mfe-core.diff --ignore-whitespace --whitespace=nowarn install dependencies:
- npm install build and run the app:
- APPMODE=development APPENV=local npm run build
- APPMODE=development APPENV=local npm run local-server (2). navbar-app: open Terminal #2 change the current dir to the navbar-app folder and apply patch:
- cd mfe-header
- git apply ../mfe-header.diff --ignore-whitespace --whitespace=nowarn there is a config file: '{navbar-app-repo}/config/development.js'. To login locally, change 'ACCOUNTS_APP_CONNECTOR', and 'AUTH' to point to the server that will be served in folder 'auth0-local-login' which was setup in the previous step.
URL: {
ACCOUNTS_APP_CONNECTOR: "http://localhost:5000",
AUTH: "http://localhost:5000"
...
}
install dependencies:
- npm install build and run the app (choose 1 out of 3 ways listed bellow): for development build with hot-reload:
- npm run dev or for development build with static server:
- APPMODE=development npm run build
- npm start or for production build with static server:
- npm run build
- npm start NOTE: Default backend environment is always the dev environment. To switch to production environment, add APPENV=production before the command, ex:
- APPENV=production npm run dev or
- APPENV=production npm run build
- npm start (3). react-app: open Terminal #3 change the current dir to the react-app folder and apply patch:
- cd micro-frontends-react-app
- git apply ../micro-frontends-react-app.diff --ignore-whitespace --whitespace=nowarn install dependencies:
- npm install build and run the app:
- npm run dev (4). angular-app: open Terminal #4 change the current dir to the angular-app folder and apply patch:
- cd micro-frontends-angular-app
- git apply ../micro-frontends-angular-app.diff --ignore-whitespace --whitespace=nowarn install dependencies:
- npm install build and run the app:
- npm run dev (5). auth0 login in local mode open Terminal #5 change the current dir to the 'auth0-local-login' folder and serve with the index.htm file
- cd auth0-local-login
- npx http-server . -p 5000 The app can be open at the browser url: 'http://localhost:5000' (this page will redirect to the actual page: 'http://localhost:3000')
-
git clone https://github.com/topcoder-platform/mfe-header.git
-
git clone https://github.com/topcoder-platform/micro-frontends-react-app.git
-
git clone https://github.com/topcoder-platform/micro-frontends-angular-app.git
and create a folder (ex: 'auth0-local-login'), and save the following file: 'https://accounts-auth0.topcoder-dev.com/setupAuth0WithRedirect.js' into that folder. After 'setupAuth0WithRedirect.js' file was saved, create an empty 'index.html' file with the following content:
<!DOCTYPE html>
<html>
<head>
<title>Auth0</title>
<meta charset="utf-8" />
<script language="javascript" type="text/javascript" src="./setupAuth0WithRedirect.js"></script>
</head>
<body>
Loaded...redirecting to auth0.(see browser console log)
<script>
window.onload = authSetup;
</script>
<a href="?retUrl=http://localhost:3000" >Login</a>
</body>
</html>
(1). root-config:
open Terminal #1
change the current dir to the root-config folder and apply patch:
-
cd mfe-core
-
git apply ../mfe-core.diff --ignore-whitespace --whitespace=nowarn
install dependencies:
- npm install
build and run the app:
-
APPMODE=development APPENV=local npm run build
-
APPMODE=development APPENV=local npm run local-server
(2). navbar-app:
open Terminal #2
change the current dir to the navbar-app folder and apply patch:
-
cd mfe-header
-
git apply ../mfe-header.diff --ignore-whitespace --whitespace=nowarn
there is a config file: '{navbar-app-repo}/config/development.js'. To login locally, change 'ACCOUNTS_APP_CONNECTOR', and 'AUTH' to point to the server that will be served in folder 'auth0-local-login' which was setup in the previous step.
URL: {
ACCOUNTS_APP_CONNECTOR: "http://localhost:5000",
AUTH: "http://localhost:5000"
...
}
install dependencies:
- npm install
build and run the app (choose 1 out of 3 ways listed bellow):
for development build with hot-reload:
- npm run dev
or
for development build with static server:
-
APPMODE=development npm run build
-
npm start
or
for production build with static server:
-
npm run build
-
npm start
NOTE:
Default backend environment is always the dev environment. To switch to production environment, add APPENV=production before the command, ex:
- APPENV=production npm run dev
or
-
APPENV=production npm run build
-
npm start
(3). react-app:
open Terminal #3
change the current dir to the react-app folder and apply patch:
-
cd micro-frontends-react-app
-
git apply ../micro-frontends-react-app.diff --ignore-whitespace --whitespace=nowarn
install dependencies:
- npm install
build and run the app:
- npm run dev
(4). angular-app:
open Terminal #4
change the current dir to the angular-app folder and apply patch:
-
cd micro-frontends-angular-app
-
git apply ../micro-frontends-angular-app.diff --ignore-whitespace --whitespace=nowarn
install dependencies:
- npm install
build and run the app:
- npm run dev
(5). auth0 login in local mode
open Terminal #5
change the current dir to the 'auth0-local-login' folder and serve with the index.htm file
-
cd auth0-local-login
-
npx http-server . -p 5000
The app can be open at the browser url: 'http://localhost:5000' (this page will redirect to the actual page: 'http://localhost:3000')