├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE
├── README.md
├── additional-assets
├── angular-ampify-add-hosting.png
├── angular-amplify-app-domain.png
├── angular-amplify-build-setting-1.png
├── angular-amplify-build-setting-2.png
├── angular-amplify-hosting-environments.png
├── angular-amplify-review-and-save.png
├── angular-amplify-setting-repository.png
├── angular-app-exposing-component.png
├── angular-charts-app-amplify-init.png
├── angular-charts-app-architecture.svg
├── angular-charts-app-in-react-container.png
├── bootstraping-angular-app.png
├── cdk-output.png
├── chart-app.png
├── microfrontend-architecture.svg
├── microfrontend-backend.svg
├── microfrontend-deployment.svg
├── mount-angular-app-on-react-container-app.png
├── react-charts-data-app-add-hosting.png
├── react-charts-data-app-amplify-init.png
├── react-charts-data-app-architecture.svg
├── react-charts-data-app-exposing-component.png
├── react-charts-data-app-hosting-environments.png
├── react-charts-in-react-container.png
├── react-container-amplify-build-setting-1.png
├── react-container-amplify-build-setting-2.png
├── react-container-amplify-setting-repository.png
├── react-container-app-add-hosting.png
├── react-container-app-amplify-app-domain.png
├── react-container-app-amplify-init.png
├── react-container-app-amplify-review-and-save.png
├── react-container-app-architecture.svg
├── react-container-app-auth.png
├── react-container-app-hosting-environments.png
├── react-container-app-importing-angular-component.png
├── react-container-app-importing-react-component.png
├── react-container-app-push-auth.png
├── react-data-app-amplify-app-domain.png
├── react-data-app-amplify-build-setting-1.png
├── react-data-app-amplify-build-setting-2.png
├── react-data-app-amplify-review-and-save.png
├── react-data-app-amplify-setting-repository.png
└── webpack-modulefederation.png
├── angular-charts-app
├── .browserslistrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── README.md
├── angular.json
├── package.json
├── src
│ ├── app
│ │ ├── app.module.ts
│ │ └── components
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.ts
│ │ │ ├── echarts-config
│ │ │ ├── initOptions.ts
│ │ │ └── options.ts
│ │ │ └── interface
│ │ │ ├── echartInitOptions.ts
│ │ │ └── inventoryStats.ts
│ ├── bootstrap.ts
│ ├── index.html
│ ├── main.ts
│ ├── mountApp.ts
│ └── polyfills.ts
├── tsconfig.app.json
├── tsconfig.json
└── webpack.config.js
├── backend
├── .gitignore
├── README.md
├── bin
│ └── microfrontend-serverless-backend.ts
├── cdk.json
├── lambda
│ ├── .gitignore
│ ├── helper.ts
│ ├── index.ts
│ ├── package.json
│ └── tsconfig.json
├── package.json
├── src
│ ├── code-commit-repository.ts
│ ├── lambda-ci-cd.ts
│ └── lambda-dynamdb-api-stack.ts
└── tsconfig.json
├── react-charts-data-app
├── .eslintignore
├── .eslintrc
├── .gitignore
├── README.md
├── package.json
├── public
│ └── index.html
├── src
│ ├── bootstrap.tsx
│ ├── components
│ │ ├── ChartData.tsx
│ │ └── style.css
│ ├── index.ts
│ └── reportWebVitals.js
├── tsconfig.json
└── webpack.config.js
└── react-container-app
├── .eslintignore
├── .eslintrc
├── .gitignore
├── README.md
├── package.json
├── public
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── favicon.ico
└── index.html
├── src
├── assets
│ └── app_logo.png
├── bootstrap.tsx
├── components
│ ├── app.tsx
│ ├── container
│ │ ├── index.tsx
│ │ └── style.css
│ ├── footer
│ │ ├── footer.css
│ │ └── footer.tsx
│ └── header
│ │ ├── header.css
│ │ └── header.tsx
├── global.d.ts
├── index.css
├── index.ts
├── reportWebVitals.js
└── utils
│ └── external-angular-app.jsx
├── tsconfig.json
└── webpack.config.js
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *main* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT No Attribution
2 |
3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17 |
18 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | MicroFrontend with AWS
2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Client-Side Rendering of Micro-Frontend on AWS using Polyglot JavaScript Frameworks
2 |
3 |
4 | In modern application development, the demand of creating a feature-rich modern web application needs the frontend application to be divided into small reusable components and developed parallelly to reduce time to market.
5 |
6 | Micro-Frontend enables us to use reusable components created using polyglot frameworks. This enabled us to create reusable components, managed by different teams in parallel, reducing time to market with a comparatively smaller codebase.
7 |
8 | Micro-Frontend helped us to achieve the following:
9 |
10 | - Reduce the time to market cost.
11 | - Easier maintenance.
12 | - Better fault tolerance.
13 | - Increased Agility and Scalability.
14 | - Independently Deployment.
15 |
16 | 
17 |
18 |
19 |
20 | ## Table of Contents
21 |
22 |
23 |
24 | #### 1. [Why Micro-Frontend](#why-micro-frontend)
25 | #### 2. [Objective of the sample application](#objective-of-the-sample-application)
26 | #### 3. [Micro-Frontend Key Concepts](#micro-frontend-key-concepts)
27 | #### 4. [Sample Overview](#sample-overview)
28 | #### 5. [Architecture Diagram](#architecture-diagram)
29 | #### 6. [Requirements](#requirements)
30 | #### 7. [Deployment of the Sample Application on AWS](#deployment-of-the-sample-application-on-aws)
31 | #### 8. [Cleanup](#cleanup)
32 | #### 9. [References](#references)
33 | #### 10. [Security](#security)
34 | #### 11. [License](#license)
35 |
36 |
37 |
38 | ## 1: Why Micro-Frontend
39 |
40 |
41 | ### a) Single Responsibility
42 | Each Module/component could be built by an individual team. The individual would be responsible for all the development, testing, release and observability of the Module/component.
43 |
44 | ### b) Technology Agnostic
45 | Modules/Components could be created in any preferred technology giving a chance to the development team to experiment and build the module optimally.
46 |
47 | ### c) Reduce Time to Market
48 | Since the Modules/Components could be built by individual teams, multiple teams could focus and develop their isolated new features/changes and release them quickly.
49 |
50 | ### d) Maintainability
51 | Micro-Frontend helps you to follow the divide and conquer approach. This ensures easily deployable, testable smaller features and the overall time for testing is reduced.
52 |
53 | ### e) Scalable development
54 | Micro-Frontend teams are smaller and can work individually without disrupting other teams. We can have a new/additional team setup that could deliver additional/new components quickly without even knowing the complexity of the complete application.
55 |
56 |
57 |
58 | ## 2: Objective of the sample application
59 |
60 |
61 |
62 | Micro-Frontend Charts Application provides a sample way to implement Micro-Frontend using polyglot JavaScript Framework (React and Angular). The sample covers the deployment of the Micro-Frontend on AWS, including the sample backend Application.
63 |
64 | ### Key Highlights of the sample Micro-Frontend App
65 |
66 | - Micro-Frontend sample implementation with React and Angular frameworks
67 | - Webpack5 and ModuleFederation for the Micro-Frontend
68 | - AWS Amplify for Frontend application deployment along with CI/CD
69 | - Amazon Cognito for the User Authentication using Amplify CLI.
70 | - AWS CodeSuite for Backend Deployment
71 | - Infrastructure automation using Cloud Development Kit (CDK)
72 |
73 |
74 |
75 | ## 3: Micro-Frontend Key Concepts
76 |
77 |
78 |
79 | ### a) Module Federation
80 |
81 | We have leveraged Webpack 5 and the Module Federation plugin to implement our Micro-Frontend Sample.
82 |
83 | Module Federation is a native plug-in for Webpack 5, that allows sharing chunks of JavaScript code between frontend applications at Run-Time. This helped the development to work in isolation on separate builds or application components. Multiple separate builds are loaded at runtime to form a single application. These separate builds act like containers and can expose and consume code between builds, creating a single, unified application.
84 |
85 | 
86 |
87 | ### b) Steps for importing React App in React Container
88 |
89 |
90 | #### i: Expose the React APP Component
91 |
92 | Expose the component from the /react-charts-data-app/webpack.config.js file
93 |
94 | 
95 |
96 | #### ii: Add React App Microservice as a remote app in the React Container
97 |
98 | React App Microservice remote in the /react-container-app/webpack.config.js file
99 |
100 | 
101 |
102 | #### iii: Use the imported component in the React Container
103 |
104 | Use the imported component within the container application in the react-container-app/src/components/container/index.tsx file
105 |
106 | 
107 |
108 |
109 |
110 | ### c) Steps for importing Angular App in React Container
111 |
112 |
113 | #### i: Bootstrap the Angular App
114 |
115 | 
116 |
117 | #### ii: Expose the Angular APP Component
118 |
119 | Expose the component from the /angular-charts-app/webpack.config.js file
120 |
121 | 
122 |
123 | #### iii: Add Angular App Microservice as a remote app in the React Container
124 |
125 | Angular App Microservice remote in the /react-container-app/webpack.config.js file
126 |
127 | 
128 |
129 | #### iv: Mount Angular App Microservice in the React Container
130 |
131 | Mount Angular App on /react-container-app/src/utils/external-angular-app.jsx file
132 |
133 | 
134 |
135 | #### v: Use the mounted component in the React Container
136 |
137 | Use the mounted component within the container application in the /react-container-app/src/components/container/index.tsx file
138 |
139 | 
140 |
141 |
142 |
143 |
144 | ## 4: Sample Overview
145 |
146 |
147 |
148 | For the Micro-Frontend sample implementation on AWS, we have chosen Angular and React frameworks to build our frontend application. The backend application is created using the NodeJS framework.
149 |
150 |
151 |
152 | ### a) Micro-Frontend Application
153 |
154 | [AWS Amplify](https://aws.amazon.com/amplify/) is leveraged to automate the process of deployment on AWS. Using AWS Amplify, we were able to deploy and add authentication to the application just by using a few user-friendly commands.
155 |
156 | Each Micro-Frontend Application is deployed using AWS Amplify which uses [Amazon S3 bucket](https://aws.amazon.com/s3/) and [Amazon CloudFront Distribution](https://aws.amazon.com/cloudfront/) internally; and for authentication [Amazon Cognito](https://aws.amazon.com/cognito/) is being leveraged.
157 |
158 | The sample consists of three applications:
159 |
160 | Micro-Frontend Apps | Purpose |
161 | |----------|-------------------------|
162 | | Angular Charts App | Contains code for generation charts |
163 | | React Charts Data App | Contains charts fitters leveraged by Angular Charts App to create charts |
164 | | React Container App | Container App containing React Charts Data App and Angular Charts App |
165 |
166 | ### b) Backend Application
167 |
168 | For the backend, we have leveraged NodeJS code deployed on [AWS Lambda functions](https://aws.amazon.com/lambda/) to persist and read data from the [AWS DynamoDB](https://aws.amazon.com/dynamodb/) leveraged as a NoSQL Database.
169 |
170 | Backend infrastructure code is automated using AWS Cloud Development Kit (AWS CDK).
171 |
172 |
173 |
174 | ## 5: Architecture Diagram
175 |
176 |
177 |
178 | [AWS Amplify](https://aws.amazon.com/amplify/) is leveraged to host our "Client Side Rendering" Frontend Application. AWS Amplify uses internally uses AWS Serverless Services like [CloudFront](https://aws.amazon.com/cloudfront/) and [S3 Bucket](https://aws.amazon.com/s3/) to deploy the frontend application.
179 |
180 | 
181 |
182 |
183 |
184 | ## 6: Requirements
185 |
186 |
187 |
188 | Name | Version |
189 | |-----------------------|----------------------------------|
190 | | [NodeJS](https://nodejs.org/en/download) | 16.17.0 |
191 | | [Typescript](https://www.typescriptlang.org/download) | 4.8.4 |
192 | | [ReactJS](https://react-cn.github.io/react/downloads.html) | 18.2.0 |
193 | | [Angular](https://angular.io/guide/setup-local) | 15.2.0 |
194 | | [CDK CLI](https://www.npmjs.com/package/cdk) | 2.92.0 |
195 | | [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) | 2.7.35 |
196 | | [AMPLIFY CLI](https://docs.amplify.aws/cli/start/install/) | 10.7.1 |
197 |
198 | ### Prerequisite verification
199 |
200 | Check if you have all the required prerequisites by executing the following commands, and they would return the versions.
201 |
202 | 1: NodeJS
203 |
204 | ```console
205 | node -v
206 | ```
207 |
208 | 2: Node
209 | ```console
210 | cdk --version
211 | ```
212 |
213 | 3: Amplify CLI
214 | ```console
215 | amplify -v
216 | ```
217 |
218 | 4: AWS CLI
219 | ```console
220 | aws --version
221 | ```
222 |
223 |
224 |
225 | ## 7: Deployment of the Sample Application on AWS
226 |
227 |
228 |
229 | In order to deploy the Micro-frontend application, you need to deploy the each applications one by one in the order mentioned below:
230 |
231 | `Note: For all the apps open new terminal and clone the repository as mentioned in the deployment of each of the below apps`
232 |
233 |
234 | ### a) [Backend App](/backend/README.md) ####
235 | ### b) [Angular Charts App](/angular-charts-app/README.md) ####
236 | ### c) [React Charts Data App](/react-charts-data-app/README.md) ####
237 | ### d) [React Container App](/react-container-app/README.md) ####
238 |
239 |
240 |
241 | Once the above steps are performed, open React Container App's Domain URL and we will get the Micro-Frontend app.
242 |
243 | 
244 |
245 |
246 |
247 | ## 8: Cleanup
248 |
249 |
250 |
251 | Cleanup requires few commands to be executed for each of the applications deployed.
252 |
253 | a) Go to `backend` directory and use `cdk destroy --all` command.
254 |
255 | b) Go to `angular-charts-app` directory and use `amplify delete` command.
256 |
257 | c) Go to `react-charts-data-app` directory and use `amplify delete` command.
258 |
259 | d) Go to `react-container-app` directory and use `amplify delete` command.
260 |
261 |
262 |
263 | ## 9: References
264 |
265 |
266 |
267 | - [Micro-frontends.org](https://micro-frontends.org/)
268 | - [Buildingmicrofrontends.com](https://www.buildingmicrofrontends.com/)
269 |
270 |
271 | ## 10: Security
272 |
273 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
274 |
275 | ## 11: License
276 |
277 |
278 | This sample code is made available under the MIT-0 license. See the LICENSE file.
279 |
--------------------------------------------------------------------------------
/additional-assets/angular-ampify-add-hosting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-ampify-add-hosting.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-app-domain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-app-domain.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-build-setting-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-build-setting-1.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-build-setting-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-build-setting-2.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-hosting-environments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-hosting-environments.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-review-and-save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-review-and-save.png
--------------------------------------------------------------------------------
/additional-assets/angular-amplify-setting-repository.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-amplify-setting-repository.png
--------------------------------------------------------------------------------
/additional-assets/angular-app-exposing-component.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-app-exposing-component.png
--------------------------------------------------------------------------------
/additional-assets/angular-charts-app-amplify-init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-charts-app-amplify-init.png
--------------------------------------------------------------------------------
/additional-assets/angular-charts-app-architecture.svg:
--------------------------------------------------------------------------------
1 |
2 | <div><b>AWS Cloud</b></div> [Not supported by viewer] [Not supported by viewer] Microfrontend - Angular Charts App
<b>Microfrontend - Angular Charts App</b> Angular App Bundle <div><b>AWS CodeCommit</b></div> Triggers Amplify Deployment
[Not supported by viewer] [Not supported by viewer] Push the code changes [Not supported by viewer] <b>CDK</b>
--------------------------------------------------------------------------------
/additional-assets/angular-charts-app-in-react-container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/angular-charts-app-in-react-container.png
--------------------------------------------------------------------------------
/additional-assets/bootstraping-angular-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/bootstraping-angular-app.png
--------------------------------------------------------------------------------
/additional-assets/cdk-output.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/cdk-output.png
--------------------------------------------------------------------------------
/additional-assets/chart-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/chart-app.png
--------------------------------------------------------------------------------
/additional-assets/mount-angular-app-on-react-container-app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/mount-angular-app-on-react-container-app.png
--------------------------------------------------------------------------------
/additional-assets/react-charts-data-app-add-hosting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-charts-data-app-add-hosting.png
--------------------------------------------------------------------------------
/additional-assets/react-charts-data-app-amplify-init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-charts-data-app-amplify-init.png
--------------------------------------------------------------------------------
/additional-assets/react-charts-data-app-architecture.svg:
--------------------------------------------------------------------------------
1 |
2 | <div><b>AWS Cloud</b></div> [Not supported by viewer] [Not supported by viewer] Microfrontend - React Charts Data App
<b>Microfrontend - React Charts Data App</b> React App Bundle <div><b>AWS CodeCommit</b></div> Triggers Amplify Deployment
[Not supported by viewer] [Not supported by viewer] Push the code changes [Not supported by viewer] <b>CDK</b>
--------------------------------------------------------------------------------
/additional-assets/react-charts-data-app-exposing-component.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-charts-data-app-exposing-component.png
--------------------------------------------------------------------------------
/additional-assets/react-charts-data-app-hosting-environments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-charts-data-app-hosting-environments.png
--------------------------------------------------------------------------------
/additional-assets/react-charts-in-react-container.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-charts-in-react-container.png
--------------------------------------------------------------------------------
/additional-assets/react-container-amplify-build-setting-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-amplify-build-setting-1.png
--------------------------------------------------------------------------------
/additional-assets/react-container-amplify-build-setting-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-amplify-build-setting-2.png
--------------------------------------------------------------------------------
/additional-assets/react-container-amplify-setting-repository.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-amplify-setting-repository.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-add-hosting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-add-hosting.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-amplify-app-domain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-amplify-app-domain.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-amplify-init.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-amplify-init.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-amplify-review-and-save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-amplify-review-and-save.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-auth.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-hosting-environments.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-hosting-environments.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-importing-angular-component.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-importing-angular-component.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-importing-react-component.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-importing-react-component.png
--------------------------------------------------------------------------------
/additional-assets/react-container-app-push-auth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-container-app-push-auth.png
--------------------------------------------------------------------------------
/additional-assets/react-data-app-amplify-app-domain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-data-app-amplify-app-domain.png
--------------------------------------------------------------------------------
/additional-assets/react-data-app-amplify-build-setting-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-data-app-amplify-build-setting-1.png
--------------------------------------------------------------------------------
/additional-assets/react-data-app-amplify-build-setting-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-data-app-amplify-build-setting-2.png
--------------------------------------------------------------------------------
/additional-assets/react-data-app-amplify-review-and-save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-data-app-amplify-review-and-save.png
--------------------------------------------------------------------------------
/additional-assets/react-data-app-amplify-setting-repository.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/react-data-app-amplify-setting-repository.png
--------------------------------------------------------------------------------
/additional-assets/webpack-modulefederation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/additional-assets/webpack-modulefederation.png
--------------------------------------------------------------------------------
/angular-charts-app/.browserslistrc:
--------------------------------------------------------------------------------
1 | # You can see what browsers were selected by your queries by running:
2 | # npx browserslist
3 |
4 | last 1 Chrome version
5 | last 1 Firefox version
6 | last 2 Edge major versions
7 | last 2 Safari major versions
8 | last 2 iOS major versions
9 | Firefox ESR
10 |
--------------------------------------------------------------------------------
/angular-charts-app/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | amplify
4 |
--------------------------------------------------------------------------------
/angular-charts-app/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "plugins": [
4 | "@typescript-eslint",
5 | "import"
6 | ],
7 | "parser": "@typescript-eslint/parser",
8 | "parserOptions": {
9 | "ecmaVersion": 2018,
10 | "sourceType": "module",
11 | "project": "./tsconfig.json"
12 | },
13 | "extends": [
14 | "plugin:import/typescript"
15 | ],
16 | "settings": {
17 | "import/parsers": {
18 | "@typescript-eslint/parser": [
19 | ".ts",
20 | ".tsx"
21 | ]
22 | },
23 | "import/resolver": {
24 | "node": {},
25 | "typescript": {
26 | "project": "./tsconfig.json",
27 | "alwaysTryTypes": true
28 | }
29 | }
30 | },
31 | "ignorePatterns": [
32 | "*.js",
33 | "*.d.ts",
34 | "node_modules/"
35 | ],
36 | "rules": {
37 | "indent": [
38 | "off"
39 | ],
40 | "@typescript-eslint/indent": [
41 | "error",
42 | 2
43 | ],
44 | "quotes": [
45 | "error",
46 | "single",
47 | {
48 | "avoidEscape": true
49 | }
50 | ],
51 | "comma-dangle": [
52 | "error",
53 | "never"
54 | ],
55 | "comma-spacing": [
56 | "error",
57 | {
58 | "before": false,
59 | "after": true
60 | }
61 | ],
62 | "no-multi-spaces": [
63 | "error",
64 | {
65 | "ignoreEOLComments": false
66 | }
67 | ],
68 | "prefer-const": ["error", {
69 | "destructuring": "any",
70 | "ignoreReadBeforeAssign": false
71 | }],
72 | "array-bracket-spacing": [
73 | "error",
74 | "never"
75 | ],
76 | "array-bracket-newline": [
77 | "error",
78 | "consistent"
79 | ],
80 | "object-curly-spacing": [
81 | "error",
82 | "always"
83 | ],
84 | "object-curly-newline": [
85 | "error",
86 | {
87 | "multiline": true,
88 | "consistent": true
89 | }
90 | ],
91 | "object-property-newline": [
92 | "error",
93 | {
94 | "allowAllPropertiesOnSameLine": true
95 | }
96 | ],
97 | "keyword-spacing": [
98 | "error"
99 | ],
100 | "brace-style": [
101 | "error",
102 | "1tbs",
103 | {
104 | "allowSingleLine": true
105 | }
106 | ],
107 | "space-before-blocks": [
108 | "error"
109 | ],
110 | "curly": [
111 | "error",
112 | "multi-line",
113 | "consistent"
114 | ],
115 | "@typescript-eslint/member-delimiter-style": [
116 | "error"
117 | ],
118 | "semi": [
119 | "error",
120 | "always"
121 | ],
122 | "max-len": [
123 | "error",
124 | {
125 | "code": 150,
126 | "ignoreUrls": true,
127 | "ignoreStrings": true,
128 | "ignoreTemplateLiterals": true,
129 | "ignoreComments": true,
130 | "ignoreRegExpLiterals": true
131 | }
132 | ],
133 | "quote-props": [
134 | "error",
135 | "consistent-as-needed"
136 | ],
137 | "@typescript-eslint/no-require-imports": [
138 | "error"
139 | ],
140 | "no-duplicate-imports": [
141 | "error"
142 | ],
143 | "no-shadow": [
144 | "off"
145 | ],
146 | "@typescript-eslint/no-shadow": [
147 | "error"
148 | ],
149 | "key-spacing": [
150 | "error"
151 | ],
152 | "no-multiple-empty-lines": [
153 | "error"
154 | ],
155 | "@typescript-eslint/no-floating-promises": [
156 | "error"
157 | ],
158 | "no-return-await": [
159 | "off"
160 | ],
161 | "@typescript-eslint/return-await": [
162 | "error"
163 | ],
164 | "no-trailing-spaces": [
165 | "error"
166 | ],
167 | "dot-notation": [
168 | "error"
169 | ],
170 | "no-bitwise": [
171 | "error"
172 | ],
173 | "@typescript-eslint/member-ordering": [
174 | "error",
175 | {
176 | "default": [
177 | "public-static-field",
178 | "public-static-method",
179 | "protected-static-field",
180 | "protected-static-method",
181 | "private-static-field",
182 | "private-static-method",
183 | "field",
184 | "constructor",
185 | "method"
186 | ]
187 | }
188 | ]
189 | }
190 | }
--------------------------------------------------------------------------------
/angular-charts-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # Compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 | /bazel-out
8 |
9 | # Node
10 | /node_modules
11 | node_modules
12 | npm-debug.log
13 | yarn-error.log
14 |
15 | # IDEs and editors
16 | .idea/
17 | .project
18 | .classpath
19 | .c9/
20 | *.launch
21 | .settings/
22 | *.sublime-workspace
23 |
24 | # Visual Studio Code
25 | .vscode/*
26 | !.vscode/settings.json
27 | !.vscode/tasks.json
28 | !.vscode/launch.json
29 | !.vscode/extensions.json
30 | .history/*
31 |
32 | # Miscellaneous
33 | /.angular/cache
34 | .sass-cache/
35 | /connect.lock
36 | /coverage
37 | /libpeerconnection.log
38 | testem.log
39 | /typings
40 |
41 | # System files
42 | .DS_Store
43 | Thumbs.db
44 |
45 | #amplify-do-not-edit-begin
46 | amplify/\#current-cloud-backend
47 | amplify/.config/local-*
48 | amplify/logs
49 | amplify/mock-data
50 | amplify/mock-api-resources
51 | amplify/backend/amplify-meta.json
52 | amplify/backend/.temp
53 | build/
54 | dist/
55 | node_modules/
56 | aws-exports.js
57 | awsconfiguration.json
58 | amplifyconfiguration.json
59 | amplifyconfiguration.dart
60 | amplify-build-config.json
61 | amplify-gradle-config.json
62 | amplifytools.xcconfig
63 | .secret-*
64 | **.sample
65 | #amplify-do-not-edit-end
66 |
--------------------------------------------------------------------------------
/angular-charts-app/README.md:
--------------------------------------------------------------------------------
1 | ### Angular Charts App Deployment
2 |
3 | Angular Charts App is deployed on AWS using AWS Amplify.
4 |
5 | CI/CD pipeline is configured using AWS Amplify.
6 |
7 | #### Prerequisite
8 |
9 | Make sure we have Amplify CLI up and running, and for installation use the [Amplify Official Website](https://docs.amplify.aws/cli/start/install/)
10 |
11 | ## Deployment on AWS
12 |
13 | 
14 |
15 | ### Deployment of the above architecture on AWS
16 |
17 | #### Step 1: Clone Repository
18 |
19 | ```console
20 | git clone https://github.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks.git
21 | cd micro-frontend-using-polyglot-javascript-frameworks/angular-charts-app
22 | ```
23 |
24 | #### Step 2: Install Dependencies
25 |
26 | ```console
27 | npm install
28 | ```
29 |
30 | ### Step 3: Initialize AWS Amplify
31 |
32 | ```console
33 | amplify init
34 | ```
35 |
36 | 
37 |
38 | ### Step 4: Add CI/CD and Deploy App
39 |
40 | ```console
41 | amplify add hosting
42 | ```
43 |
44 | Amplify hosting will ask a few questions, select the answers as present in the screenshot:
45 |
46 | 
47 |
48 | *After selecting the options, Amplify CLI would open Amplify console on the default web browser.*
49 |
50 | #### Step 4a: Setting up Hosting Environment
51 |
52 | Click on *Hosting environments* and Select *AWS CodeCommit*. Click "Connect Branch"
53 |
54 | 
55 |
56 | #### Step 4b: Setting up Repository
57 |
58 | Select *microfrontend-angular-charts-app* repository from the drop down and select *main* branch.
59 |
60 | 
61 |
62 | #### Step 4c: Configure the Build Setting
63 |
64 | - Select *Environment* as **Dev** from the drop down
65 | - Select an existing service role or create a new one so Amplify Hosting may access your resources.
66 | - Click on *Advanced settings* drop down and set the environment variable **CHARTS_BACKEND_API** (Copy the value of ***APIGatewayURL***) *[(can get from Step 3 in the backend app deployment)](../backend/README.md)*)
67 |
68 | 
69 | 
70 |
71 | #### Step 4d: Setting up Build Setting
72 |
73 | Review the setting and click on *Save and Deploy*
74 |
75 | 
76 |
77 | ### Step 5: Getting the Angular Charts App endpoint
78 |
79 | After completing *Step 4d*, come back to the existing terminal and hit enter. You can see the Frontend App Domain. Save this Domain in a text editor, we would be need with when deploying the React Container App.
80 |
81 | 
82 |
83 | [Lets' deploy our React Charts Data App.](../react-charts-data-app/README.md)
84 |
85 | [Go Back.](../README.md)
--------------------------------------------------------------------------------
/angular-charts-app/angular.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3 | "version": 1,
4 | "newProjectRoot": "projects",
5 | "projects": {
6 | "angular-charts-app": {
7 | "projectType": "application",
8 | "schematics": {
9 | "@schematics/angular:component": {
10 | "style": "scss"
11 | },
12 | "@schematics/angular:application": {
13 | "strict": true
14 | }
15 | },
16 | "root": "",
17 | "sourceRoot": "src",
18 | "prefix": "app",
19 | "architect": {
20 | "build": {
21 | "builder": "ngx-build-plus:browser",
22 | "options": {
23 | "outputPath": "dist/",
24 | "index": "src/index.html",
25 | "main": "src/main.ts",
26 | "polyfills": "src/polyfills.ts",
27 | "tsConfig": "tsconfig.app.json",
28 | "inlineStyleLanguage": "scss",
29 | "assets": [
30 | "src/favicon.ico",
31 | "src/assets"
32 | ],
33 | "styles": [],
34 | "scripts": [],
35 | "extraWebpackConfig": "webpack.config.js",
36 | "commonChunk": false
37 | },
38 | "configurations": {
39 | "production": {
40 | "budgets": [
41 | {
42 | "type": "initial",
43 | "maximumWarning": "500kb",
44 | "maximumError": "1mb"
45 | },
46 | {
47 | "type": "anyComponentStyle",
48 | "maximumWarning": "2kb",
49 | "maximumError": "4kb"
50 | }
51 | ],
52 | "outputHashing": "all",
53 | "extraWebpackConfig": "webpack.config.js"
54 | },
55 | "development": {
56 | "buildOptimizer": false,
57 | "optimization": false,
58 | "vendorChunk": true,
59 | "extractLicenses": false,
60 | "sourceMap": true,
61 | "namedChunks": true
62 | }
63 | },
64 | "defaultConfiguration": "production"
65 | },
66 | "serve": {
67 | "builder": "ngx-build-plus:dev-server",
68 | "configurations": {
69 | "production": {
70 | "browserTarget": "angular-charts-app:build:production",
71 | "extraWebpackConfig": "webpack.config.js"
72 | },
73 | "development": {
74 | "browserTarget": "angular-charts-app:build:development"
75 | }
76 | },
77 | "defaultConfiguration": "development",
78 | "options": {
79 | "port": 8082,
80 | "publicHost": "http://localhost:8082",
81 | "extraWebpackConfig": "webpack.config.js"
82 | }
83 | },
84 | "extract-i18n": {
85 | "builder": "ngx-build-plus:extract-i18n",
86 | "options": {
87 | "browserTarget": "angular-charts-app:build",
88 | "extraWebpackConfig": "webpack.config.js"
89 | }
90 | }
91 | }
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/angular-charts-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "angular-charts-app",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "clean": "rm -rf node_modules dist",
6 | "start": "npm run lint && npx kill-port 8082 && ng serve",
7 | "build": "npm run lint && ng build",
8 | "lint": "npx eslint . --ext .ts,.tsx --fix",
9 | "watch": "ng build --watch --configuration development",
10 | "run:all": "node node_modules/@angular-architects/module-federation/src/server/mf-dev-server.js"
11 | },
12 | "private": true,
13 | "dependencies": {
14 | "@angular-architects/module-federation": "^15.0.3",
15 | "@angular/animations": "^15.2.0",
16 | "@angular/common": "^15.2.0",
17 | "@angular/compiler": "^15.2.0",
18 | "@angular/core": "^15.2.0",
19 | "@angular/forms": "^15.2.0",
20 | "@angular/platform-browser": "^15.2.0",
21 | "@angular/platform-browser-dynamic": "^15.2.0",
22 | "@angular/router": "^15.2.0",
23 | "echarts": "^5.4.1",
24 | "ngx-build-plus": "^15.0.0",
25 | "ngx-echarts": "^15.0.1",
26 | "rxjs": "~7.8.0",
27 | "tslib": "^2.3.0",
28 | "zone.js": "~0.12.0"
29 | },
30 | "devDependencies": {
31 | "@angular-devkit/build-angular": "^15.2.1",
32 | "@angular/cli": "~15.2.1",
33 | "@angular/compiler-cli": "^15.2.0",
34 | "@typescript-eslint/eslint-plugin": "^5.57.1",
35 | "@typescript-eslint/parser": "^5.57.1",
36 | "eslint": "^8.37.0",
37 | "eslint-plugin-import": "^2.27.5",
38 | "kill-port": "^2.0.1",
39 | "typescript": "~4.9.4"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/angular-charts-app/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { HttpClientModule } from '@angular/common/http';
2 | import { NgModule } from '@angular/core';
3 | import { BrowserModule } from '@angular/platform-browser';
4 | import { NgxEchartsModule } from 'ngx-echarts';
5 | import { AppComponent } from './components/app.component';
6 |
7 | @NgModule({
8 | declarations: [
9 | AppComponent
10 | ],
11 | imports: [
12 | NgxEchartsModule.forRoot({
13 | echarts: () => import('echarts')
14 | }),
15 | BrowserModule,
16 | HttpClientModule
17 | ],
18 | providers: [],
19 | bootstrap: [AppComponent]
20 | })
21 |
22 | export class AppModule {}
23 |
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Product Categories Data
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/app.component.scss:
--------------------------------------------------------------------------------
1 | .angularChartsApp {
2 | text-align: center;
3 | padding-bottom: 6%;
4 | background-color: rgb(248 238 238);
5 | }
6 |
7 | .angularCharts {
8 | margin-top: -4%;
9 | padding-left: 0%;
10 | }
11 |
12 | .angularChartsAppText {
13 | padding-top: 4%;
14 | color: crimson;
15 | text-align: center;
16 | }
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/app.component.ts:
--------------------------------------------------------------------------------
1 | import { HttpClient } from '@angular/common/http';
2 | import { Component, OnInit } from '@angular/core';
3 | import { EChartsOption } from 'echarts';
4 |
5 | // Importing EChart configuration
6 | import { initOptions } from './echarts-config/initOptions';
7 | import { eChartOption } from './echarts-config/options';
8 |
9 | // Importing interfaces
10 | import { EChartsInitOptions } from './interface/echartInitOptions';
11 | import { InventoryStats } from './interface/inventoryStats';
12 |
13 | declare const CHARTS_BACKEND_API_URL: string;
14 | const backendAPI = CHARTS_BACKEND_API_URL || 'http://localhost:3000';
15 |
16 | @Component({
17 | selector: 'app-root',
18 | templateUrl: './app.component.html',
19 | styleUrls: ['./app.component.scss']
20 | })
21 |
22 | export class AppComponent implements OnInit {
23 | title = 'angular-charts-app';
24 |
25 | initOpts: EChartsInitOptions = initOptions;
26 | options: EChartsOption = eChartOption;
27 | mergeOptions = {};
28 |
29 | constructor(private http : HttpClient) {}
30 |
31 | updateChartsData(userId: string) {
32 | this.http.get(`${backendAPI}/charts/${userId}`)
33 | .subscribe(Response => {
34 | const API_Response: any = Response;
35 |
36 | if (Response && API_Response) {
37 |
38 | // Formating data according to Charts
39 | const formatedData: Object = this.formatData(API_Response);
40 |
41 | // Updating Charts
42 | this.mergeOptions = formatedData;
43 | }
44 | });
45 | }
46 |
47 | ngOnInit() {
48 | const userDataKey = localStorage.getItem('userDataKey') || '0';
49 | const user = JSON.parse(localStorage.getItem(userDataKey) || '{}');
50 | this.updateChartsData(user.Username);
51 |
52 | // Added a event listener to update the charts based on the data received on the 'CHARTS_UPDATE_EVENT' event
53 | window.addEventListener('CHARTS_UPDATE_EVENT', (customEvent: any) => {
54 | this.updateChartsData(user.Username);
55 | }, {
56 | passive: true
57 | });
58 | }
59 |
60 | formatData(inventoryStats: InventoryStats): Object {
61 |
62 | // Clubbing shopping categories
63 | const electronicsStats = Number(inventoryStats.mobilePhones) + Number(inventoryStats.cameras) + Number(inventoryStats.laptops);
64 | const apparel = Number(inventoryStats.jeans) + Number(inventoryStats.shirts) + Number(inventoryStats.shoes);
65 | const furniture = Number(inventoryStats.chairs) + Number(inventoryStats.tables) + Number(inventoryStats.wardrobes);
66 |
67 | const formatedData = {
68 | series: [{
69 | data: [
70 | { value: electronicsStats, name: 'Electronics' },
71 | { value: apparel, name: 'Apparel' },
72 | { value: furniture, name: 'Furniture' }
73 | ]
74 | }, {
75 | data: [
76 | { value: inventoryStats.mobilePhones, name: 'Mobile Phones' },
77 | { value: inventoryStats.cameras, name: 'Cameras' },
78 | { value: inventoryStats.laptops, name: 'Laptops' },
79 | { value: inventoryStats.jeans, name: 'Jeans' },
80 | { value: inventoryStats.shirts, name: 'Shirts' },
81 | { value: inventoryStats.shoes, name: 'Shoes' },
82 | { value: inventoryStats.chairs, name: 'Chairs' },
83 | { value: inventoryStats.tables, name: 'Tables' },
84 | { value: inventoryStats.wardrobes, name: 'Wardrobes' }
85 | ]
86 | }]
87 | };
88 | return formatedData;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/echarts-config/initOptions.ts:
--------------------------------------------------------------------------------
1 | import { EChartsInitOptions } from '../interface/echartInitOptions';
2 |
3 | export const initOptions: EChartsInitOptions = {
4 | renderer: 'svg',
5 | width: 850,
6 | height: 700
7 | };
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/echarts-config/options.ts:
--------------------------------------------------------------------------------
1 | import type { EChartsOption } from 'echarts';
2 |
3 | export const eChartOption: EChartsOption = {
4 | tooltip: {
5 | trigger: 'item',
6 | formatter: '{a} {b}: {c} ({d}%)'
7 | },
8 | legend: {
9 | bottom: 5,
10 | align: 'auto',
11 | data: [
12 | 'Electronics',
13 | 'Apparel',
14 | 'Furniture',
15 | 'Mobile Phones',
16 | 'Cameras',
17 | 'Laptops',
18 | 'Jeans',
19 | 'Shirts',
20 | 'Shoes',
21 | 'Chairs',
22 | 'Tables',
23 | 'Wardrobes'
24 | ]
25 | },
26 | series: [
27 | {
28 | name: 'Prodcut Categories',
29 | type: 'pie',
30 | selectedMode: 'single',
31 | radius: [0, '35%'],
32 | label: {
33 | position: 'inner',
34 | fontSize: 12
35 | },
36 | labelLine: {
37 | show: false
38 | },
39 | data: [
40 | { value: 0, name: 'Electronics' },
41 | { value: 0, name: 'Apparel' },
42 | { value: 0, name: 'Furniture' }
43 | ]
44 | },
45 | {
46 | name: 'Product',
47 | type: 'pie',
48 | radius: ['45%', '60%'],
49 | labelLine: {
50 | length: 30
51 | },
52 | label: {
53 | formatter: '{a|{a}}{abg|}\n{hr|}\n {b|{b}:}{c} {per|{d}%} ',
54 | backgroundColor: '#F6F8FC',
55 | borderColor: '#8C8D8E',
56 | borderWidth: 1,
57 | borderRadius: 4,
58 | rich: {
59 | a: {
60 | color: '#6E7079',
61 | lineHeight: 22,
62 | align: 'center'
63 | },
64 | hr: {
65 | borderColor: '#8C8D8E',
66 | width: '100%',
67 | borderWidth: 1,
68 | height: 0
69 | },
70 | b: {
71 | color: '#4C5058',
72 | fontSize: 12,
73 | fontWeight: 'bold',
74 | lineHeight: 33
75 | },
76 | per: {
77 | color: '#fff',
78 | backgroundColor: '#4C5058',
79 | padding: [3, 4],
80 | borderRadius: 4
81 | }
82 | }
83 | },
84 | data: [
85 | { value: 0, name: 'Mobile Phones' },
86 | { value: 0, name: 'Cameras' },
87 | { value: 0, name: 'Laptops' },
88 | { value: 0, name: 'Jeans' },
89 | { value: 0, name: 'Shirts' },
90 | { value: 0, name: 'Shoes' },
91 | { value: 0, name: 'Chairs' },
92 | { value: 0, name: 'Tables' },
93 | { value: 0, name: 'Wardrobes' }
94 | ]
95 | }
96 | ]
97 | };
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/interface/echartInitOptions.ts:
--------------------------------------------------------------------------------
1 | export interface EChartsInitOptions {
2 | renderer: string;
3 | width: number;
4 | height: number;
5 | }
--------------------------------------------------------------------------------
/angular-charts-app/src/app/components/interface/inventoryStats.ts:
--------------------------------------------------------------------------------
1 | export interface InventoryStats {
2 | mobilePhones: number;
3 | cameras: number;
4 | laptops: number;
5 | chairs: number;
6 | tables: number;
7 | wardrobes: number;
8 | jeans: number;
9 | shirts: number;
10 | shoes: number;
11 | }
--------------------------------------------------------------------------------
/angular-charts-app/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | import { enableProdMode } from '@angular/core';
2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
3 |
4 | import { AppModule } from './app/app.module';
5 |
6 | enableProdMode();
7 |
8 | platformBrowserDynamic().bootstrapModule(AppModule)
9 | .catch(err => console.error(err));
10 |
--------------------------------------------------------------------------------
/angular-charts-app/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | chartsApp
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/angular-charts-app/src/main.ts:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 |
--------------------------------------------------------------------------------
/angular-charts-app/src/mountApp.ts:
--------------------------------------------------------------------------------
1 | import 'zone.js';
2 | import { enableProdMode } from '@angular/core';
3 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4 |
5 | import { AppModule } from './app/app.module';
6 |
7 | enableProdMode();
8 |
9 | interface awsCognitoDetails {
10 | userDataKey: string;
11 | };
12 |
13 | const mount = (cognitoDetails: awsCognitoDetails) => {
14 | localStorage.setItem('userDataKey', cognitoDetails.userDataKey);
15 | platformBrowserDynamic().bootstrapModule(AppModule)
16 | .catch(err => console.error(err));
17 | };
18 |
19 | export { mount };
--------------------------------------------------------------------------------
/angular-charts-app/src/polyfills.ts:
--------------------------------------------------------------------------------
1 | import 'zone.js'; // Included with Angular CLI.
2 |
--------------------------------------------------------------------------------
/angular-charts-app/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */
2 | {
3 | "extends": "./tsconfig.json",
4 | "compilerOptions": {
5 | "outDir": "./out-tsc/app",
6 | "types": []
7 | },
8 | "files": [
9 | "src/main.ts",
10 | "src/polyfills.ts",
11 | "src/mountApp.ts"
12 | ],
13 | "include": [
14 | "src/**/*.d.ts"
15 | ]
16 | }
--------------------------------------------------------------------------------
/angular-charts-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "outDir": "./dist/out-tsc",
6 | "forceConsistentCasingInFileNames": true,
7 | "strict": true,
8 | "noImplicitOverride": true,
9 | "noPropertyAccessFromIndexSignature": true,
10 | "noImplicitReturns": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "sourceMap": true,
13 | "declaration": false,
14 | "downlevelIteration": true,
15 | "experimentalDecorators": true,
16 | "moduleResolution": "node",
17 | "importHelpers": true,
18 | "target": "ES2022",
19 | "module": "es2020",
20 | "lib": [
21 | "ES2022",
22 | "dom"
23 | ]
24 | },
25 | "angularCompilerOptions": {
26 | "enableI18nLegacyMessageIdFormat": false,
27 | "strictInjectionParameters": true,
28 | "strictInputAccessModifiers": true,
29 | "strictTemplates": true
30 | }
31 | }
--------------------------------------------------------------------------------
/angular-charts-app/webpack.config.js:
--------------------------------------------------------------------------------
1 | const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");
2 | const mf = require("@angular-architects/module-federation/webpack");
3 | const path = require("path");
4 | const webpack = require("webpack");
5 | const share = mf.share;
6 |
7 | const sharedMappings = new mf.SharedMappings();
8 | sharedMappings.register(
9 | path.join(__dirname, 'tsconfig.json'),
10 | [/* mapped paths to share */]);
11 |
12 | module.exports = {
13 | output: {
14 | uniqueName: "chartsApp",
15 | publicPath: "auto",
16 | scriptType: 'text/javascript'
17 | },
18 | optimization: {
19 | runtimeChunk: false
20 | },
21 | resolve: {
22 | alias: {
23 | ...sharedMappings.getAliases(),
24 | }
25 | },
26 | experiments: {
27 | outputModule: true
28 | },
29 | plugins: [
30 | new ModuleFederationPlugin({
31 | name: "angularChartsApp",
32 | filename: "remoteEntry.js",
33 | exposes: {
34 | './angularChartsApp': './src/mountApp.ts'
35 | },
36 | shared: share({
37 | "@angular/core": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
38 | "@angular/common": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
39 | "@angular/common/http": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
40 | "@angular/router": { singleton: true, strictVersion: true, requiredVersion: 'auto' },
41 |
42 | ...sharedMappings.getDescriptors()
43 | })
44 |
45 | }),
46 | sharedMappings.getPlugin(),
47 | new webpack.DefinePlugin({
48 | 'CHARTS_BACKEND_API_URL': JSON.stringify(`${process.env.CHARTS_BACKEND_API}`),
49 | })
50 | ],
51 | };
52 |
--------------------------------------------------------------------------------
/backend/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | !jest.config.js
3 | *.d.ts
4 | node_modules
5 | .angular
6 | .vscode
7 | .editorconfig
8 |
9 | # CDK asset staging directory
10 | .cdk.staging
11 | cdk.out
12 |
--------------------------------------------------------------------------------
/backend/README.md:
--------------------------------------------------------------------------------
1 | # Backend for the Micro-Frontend Application
2 |
3 | The Backend Application contains API code for the Micro-Frontend Application along the infrastructure as a code for creating AWS resources.
4 |
5 | API Code is written in NodeJS and deployed on AWS Lambda. APIs are responsible for creating and updating records on the DynamoDB Table.
6 |
7 | Infrastructure automation is performed using AWS CDK written in Typescript. CDK contains the code for AWS resources (AWS CodeCommit Repository for Backend app and Micro-Frontends, AWS CodeBuild, AWS CodePipeline, AWS DynamoDB).
8 |
9 | ## Essential Components
10 |
11 | ### 1: API Definition
12 |
13 | | API | HTTP Verb | Purpose |
14 | |------------------------|----------------------|--------------------------------------|
15 | | /charts/:id | GET | Leveraged to get chart data for a user |
16 | | /charts | POST | Leveraged to update/create chart data for a user |
17 |
18 | ### 2: DynamoDB Table Definition
19 |
20 | | Key | Data Type |
21 | |----------|-------------------------|
22 | | userId | Sting(PK) |
23 | | cameras | Number |
24 | | chairs | Number |
25 | | jeans | Number |
26 | | mobilePhones | Number |
27 | | shirts | Number |
28 | | shoes | Number |
29 | | tables | Number |
30 | | wardrobes | Number |
31 |
32 | ### 3: CDK code for AWS Resources
33 |
34 | | Resources | Purpose |
35 | |----------|-------------------------|
36 | | AWS CodeCommit | Code Repository for Backend App and Micro-Frontend Apps |
37 | | AWS Build | Leveraged for Building and Deploying code on AWS Lambda function |
38 | | AWS CodePipeline | Leveraged to automate release pipeline |
39 | | AWS Lambda Function | Leveraged to deploy backend code |
40 | | AWS API Gateway | Front door and trigger for Lambda Function |
41 |
42 | ## Deployment on AWS
43 |
44 | 
45 |
46 | ### Deployment of the above architecture on AWS
47 |
48 | #### Step 1: Clone Repository
49 |
50 | ```console
51 | git clone https://github.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks.git
52 | cd micro-frontend-using-polyglot-javascript-frameworks/backend
53 | ```
54 |
55 | #### Step 2: Install Dependencies
56 |
57 | ```console
58 | npm install
59 | ```
60 |
61 | #### Step 3: Compile Lambda function code (TypeScript to Javascript)
62 |
63 | ```console
64 | npm run build:lambda
65 | ```
66 |
67 | #### Step 4: Bootstrap Environment (Optional)
68 |
69 | Bootstrap environment is requried only for the first time when are running CDK on the AWS account. Bootstrapping creates resources that may be needed to deploy your stack like an Amazon S3 bucket for storing files and IAM roles that grant permissions needed to perform deployments.
70 |
71 | ```console
72 | cdk bootstrap
73 | ```
74 |
75 | #### Step 5: Deploy infrastructure on AWS
76 |
77 | ```console
78 | npm run deploy
79 | ```
80 |
81 | #### Step 6: Getting the Backend API URI
82 |
83 | Once the CDK deployment is successful, go to the *cdk-outputs.json* file present in the backend app, and copy the value of "APIGatewayURL". We would need API URL for the frontend apps.
84 |
85 | 
86 |
87 | After completing the Backend App deployment, we can proceed with the Frontend Applications deployment.
88 |
89 | [Let's deploy our first Micro-Frontend app, Angular Charts App.](../angular-charts-app/README.md)
90 |
91 | [Go Back.](../README.md)
--------------------------------------------------------------------------------
/backend/bin/microfrontend-serverless-backend.ts:
--------------------------------------------------------------------------------
1 | import * as cdk from 'aws-cdk-lib';
2 | import { LambdaDynamoDBApiStack } from '../src/lambda-dynamdb-api-stack';
3 | import { CodeRepositoryStack } from '../src/code-commit-repository';
4 | import { LambdaCICDStack } from '../src/lambda-ci-cd';
5 |
6 | const app = new cdk.App();
7 |
8 | // Backend Services for the Microfrontend App
9 | // Services - AWS Lambda, Amazon API Gateway and Amazon Dynamodb
10 | const lambdaFunction = new LambdaDynamoDBApiStack(app, 'Microfrontend-Backend', {
11 | stackName: 'Microfrontend-Backend',
12 | dynamoDBTable: 'microfrontend-charts'
13 | });
14 |
15 | // // Code Repository for the Microfrontend - React Container App
16 | new CodeRepositoryStack(app, 'Microfrontend-Container-App-CodeRepository', {
17 | stackName: 'microfrontend-react-container-app',
18 | repositoryName: 'microfrontend-react-container-app',
19 | sourceCodePath: '../react-container-app/'
20 | })
21 |
22 | // Code Repository for the Microfrontend - React Charts Data App
23 | new CodeRepositoryStack(app, 'Microfrontend-React-Charts-Data-App-CodeRepository', {
24 | stackName: 'microfrontend-react-charts-data-app',
25 | repositoryName: 'microfrontend-react-charts-data-app',
26 | sourceCodePath: '../react-charts-data-app/'
27 | })
28 |
29 | // Code Repository for the Microfrontend - Microfrontend Angular Charts App
30 | new CodeRepositoryStack(app, 'Microfrontend-Angular-Charts-App-CodeRepository', {
31 | stackName: 'microfrontend-angular-charts-app',
32 | repositoryName: 'microfrontend-angular-charts-app',
33 | sourceCodePath: '../angular-charts-app/'
34 | })
35 |
36 | // Code Repository for the Backend Lambda function
37 | const lambdaFunctionRepository = new CodeRepositoryStack(app, 'Microfrontend-Backend-Lambda-Function-CodeRepository', {
38 | stackName: 'microfrontend-backend-lambda-function',
39 | repositoryName: 'microfrontend-backend-lambda-function',
40 | sourceCodePath: './lambda/'
41 | })
42 |
43 | // Setting up the CI/CD for the Backend Lambda function using AWS Code Suite
44 | new LambdaCICDStack(app, 'Microfrontend-Backend-Lambda-CICD', {
45 | stackName: 'microfrontend-backend-lambda-cicd',
46 | repository: lambdaFunctionRepository.repository,
47 | lambdaFunction: lambdaFunction.lambdaFunction,
48 | })
49 |
50 | app.synth();
--------------------------------------------------------------------------------
/backend/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "npx ts-node --prefer-ts-exts bin/microfrontend-serverless-backend.ts",
3 | "watch": {
4 | "include": [
5 | "**",
6 | "../lambda/*"
7 | ],
8 | "exclude": [
9 | "README.md",
10 | "cdk*.json",
11 | "**/*.d.ts",
12 | "**/*.js",
13 | "tsconfig.json",
14 | "package*.json",
15 | "yarn.lock",
16 | "node_modules"
17 | ]
18 | },
19 | "context": {
20 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true,
21 | "@aws-cdk/core:stackRelativeExports": true,
22 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true,
23 | "@aws-cdk/aws-lambda:recognizeVersionProps": true,
24 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
25 | "@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true,
26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
28 | "@aws-cdk/core:checkSecretUsage": true,
29 | "@aws-cdk/aws-iam:minimizePolicies": true,
30 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
31 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true,
32 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
33 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
34 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
35 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
36 | "@aws-cdk/core:enablePartitionLiterals": true,
37 | "@aws-cdk/core:target-partitions": [
38 | "aws",
39 | "aws-cn"
40 | ]
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/backend/lambda/.gitignore:
--------------------------------------------------------------------------------
1 | *.js
2 | !jest.config.js
3 | *.d.ts
4 | node_modules
5 | dist
--------------------------------------------------------------------------------
/backend/lambda/helper.ts:
--------------------------------------------------------------------------------
1 | export function getChartsDataQuery(userId: String): any{
2 | return {
3 | TableName: process.env.TABLE_NAME || 'charts',
4 | Key: {userId: userId}
5 | }
6 | }
7 |
8 | export function updateChartsDataQuery(chartsData: any): any{
9 | return {
10 | TableName: process.env.TABLE_NAME || 'charts',
11 | Key: {userId: chartsData.userId},
12 | UpdateExpression: `set mobilePhones = :mobilePhones ,
13 | cameras = :cameras , laptops = :laptops ,
14 | chairs = :chairs , tables = :tables ,
15 | wardrobes = :wardrobes , jeans = :jeans ,
16 | shirts = :shirts , shoes = :shoes`,
17 | ExpressionAttributeValues: {
18 | ":mobilePhones": chartsData.mobilePhones,
19 | ":cameras": chartsData.cameras,
20 | ":laptops": chartsData.laptops,
21 | ":chairs": chartsData.chairs,
22 | ":tables": chartsData.tables,
23 | ":wardrobes": chartsData.wardrobes,
24 | ":jeans": chartsData.jeans,
25 | ":shirts": chartsData.shirts,
26 | ":shoes": chartsData.shoes,
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/backend/lambda/index.ts:
--------------------------------------------------------------------------------
1 | import { Handler } from 'aws-lambda';
2 | import serverless from 'serverless-http';
3 | import express from 'express';
4 | import * as AWS from 'aws-sdk';
5 |
6 | // Using the helper functions
7 | import * as helpers from './helper';
8 |
9 | const app = express();
10 | app.use(express.urlencoded({ extended: true }));
11 | app.use(express.json());
12 |
13 | // Creating AWS DynamoDB client
14 | const dynamo = new AWS.DynamoDB.DocumentClient();
15 |
16 | // Get API
17 | app.get('/charts/:userId', async (req, res) => {
18 |
19 | res.setHeader('Access-Control-Allow-Origin', '*');
20 | res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
21 |
22 | // Getting userId from pathParameters
23 | const userId = req.params.userId;
24 |
25 | // Setting up DynamoDB query
26 | const findQueryParams = helpers.getChartsDataQuery(userId)
27 |
28 | try {
29 | // Getting chart data from DynamoDB
30 | const result = await dynamo.get(findQueryParams).promise();
31 |
32 | // Returning result if Item is found
33 | if(result.Item){
34 | res.status(200).send(JSON.stringify(result.Item))
35 | } else {
36 | res.status(204).send(JSON.stringify({}))
37 | }
38 | } catch (err) {
39 | // Returning generic error message if something goes wrong
40 | res.status(500).send('Something went wrong!')
41 | }
42 | });
43 |
44 | // Post API
45 | app.post('/charts', async (req, res) => {
46 |
47 | res.setHeader('Access-Control-Allow-Origin', '*');
48 | res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
49 |
50 |
51 | // Fetching body from the API request
52 | let chartsData = typeof req.body == 'object' ? req.body : JSON.parse(req.body);
53 |
54 | // Setting up DynamoDB Query parameters
55 | const findQueryParams = helpers.getChartsDataQuery(chartsData.userId)
56 |
57 | // Setting up DynamoDB Update parameters
58 | const updateQueryParams = helpers.updateChartsDataQuery(chartsData)
59 |
60 | try {
61 |
62 | // Checking if the chart data exists for the user
63 | let data = await dynamo.get(findQueryParams).promise();
64 |
65 | // Updating the charts data if data is found
66 | if(data.Item) {
67 | await dynamo.update(updateQueryParams).promise();
68 | } else { // Creating the charts data if data is not found
69 |
70 | // Setting up DynamoDB Query parameters for creating the charts data
71 | const createQueryParams = {
72 | TableName: process.env.TABLE_NAME || 'charts',
73 | Item: chartsData
74 | }
75 |
76 | await dynamo.put(createQueryParams).promise();
77 | }
78 |
79 | // Returning success message
80 | res.status(200).send('ChartsData saved successfully')
81 | } catch(err) {
82 | // Returning generic error message if something goes wrong
83 | res.status(500).send('Something went wrong!')
84 | }
85 | })
86 |
87 | export const handler: Handler = serverless(app)
--------------------------------------------------------------------------------
/backend/lambda/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "charts-data-lambda",
3 | "version": "1.0.0",
4 | "description": "Lambda function to get data from Charts table",
5 | "private": true,
6 | "scripts": {
7 | "build": "esbuild index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outdir=./dist/"
8 | },
9 | "devDependencies": {
10 | "@types/aws-lambda": "^8.10.114",
11 | "@types/express": "^4.17.17",
12 | "@types/node": "*",
13 | "esbuild": "^0.17.18"
14 | },
15 | "dependencies": {
16 | "aws-lambda": "^1.0.7",
17 | "aws-sdk": "*",
18 | "express": "^4.18.2",
19 | "serverless-http": "^3.2.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/backend/lambda/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2018",
4 | "module": "commonjs",
5 | "outDir": "lib",
6 | "lib": [
7 | "es2018"
8 | ],
9 | "declaration": true,
10 | "strict": true,
11 | "noImplicitAny": true,
12 | "esModuleInterop": true,
13 | "strictNullChecks": true,
14 | "noImplicitThis": true,
15 | "alwaysStrict": true,
16 | "noUnusedLocals": false,
17 | "noUnusedParameters": false,
18 | "noImplicitReturns": true,
19 | "noFallthroughCasesInSwitch": false,
20 | "inlineSourceMap": true,
21 | "inlineSources": true,
22 | "experimentalDecorators": true,
23 | "strictPropertyInitialization": false,
24 | "typeRoots": [
25 | "./node_modules/@types"
26 | ]
27 | },
28 | "exclude": [
29 | "node_modules"
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "microfrontend-serverless-backend",
3 | "version": "0.1.0",
4 | "bin": {
5 | "microfrontend-serverless-backend": "bin/microfrontend-serverless-backend.js"
6 | },
7 | "workspaces": [
8 | "lambda"
9 | ],
10 | "scripts": {
11 | "build": "tsc",
12 | "watch": "tsc -w",
13 | "lint": "npx eslint",
14 | "deploy": "npm run build:lambda && npx eslint && cdk deploy --all --outputs-file ./cdk-outputs.json",
15 | "build:lambda": "esbuild ./lambda/index.ts --bundle --minify --sourcemap --platform=node --target=es2020 --outdir=./lambda-compiled-code/",
16 | "cdk": "npx eslint && cdk",
17 | "clean": "rm -rf node_modules lib lambda-compiled-code cdk-outputs.json"
18 | },
19 | "devDependencies": {
20 | "@types/node": "10.17.27",
21 | "@types/prettier": "2.6.0",
22 | "aws-cdk": "^2.81.0",
23 | "esbuild": "^0.17.18",
24 | "eslint": "^8.39.0",
25 | "eslint-config-semistandard": "^17.0.0",
26 | "eslint-config-standard": "^17.0.0",
27 | "eslint-plugin-import": "^2.27.5",
28 | "eslint-plugin-n": "^15.7.0",
29 | "eslint-plugin-promise": "^6.1.1",
30 | "ts-node": "^10.9.1",
31 | "typescript": "~3.9.7"
32 | },
33 | "dependencies": {
34 | "aws-cdk-lib": "^2.81.0",
35 | "constructs": "^10.0.0",
36 | "source-map-support": "^0.5.21"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/backend/src/code-commit-repository.ts:
--------------------------------------------------------------------------------
1 | import { Stack, StackProps } from 'aws-cdk-lib';
2 | import { Construct } from 'constructs';
3 | import * as codecommit from 'aws-cdk-lib/aws-codecommit';
4 |
5 | interface CodeRepositoryStackProps extends StackProps {
6 | stackName?: string,
7 | repositoryName?: string
8 | sourceCodePath?: string
9 | }
10 |
11 | // Stack to setup CodeCommit repository
12 | export class CodeRepositoryStack extends Stack {
13 | public readonly repository: codecommit.IRepository;
14 | constructor(scope: Construct, id: string, props?: CodeRepositoryStackProps) {
15 | super(scope, id, props);
16 |
17 | // New Repository and pushing code from source directory
18 | this.repository = new codecommit.Repository(this, `${props?.stackName}-repository`, {
19 | repositoryName: props?.repositoryName || 'code-repository',
20 | code: codecommit.Code.fromDirectory(`${props?.sourceCodePath}`, 'main')
21 | });
22 | }
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/backend/src/lambda-ci-cd.ts:
--------------------------------------------------------------------------------
1 | import { Stack, StackProps } from 'aws-cdk-lib';
2 | import { Construct } from 'constructs';
3 | import {CodeBuildStep, CodePipeline, CodePipelineSource} from "aws-cdk-lib/pipelines";
4 | import * as codecommit from 'aws-cdk-lib/aws-codecommit';
5 | import * as iam from 'aws-cdk-lib/aws-iam'
6 | import * as lambda from 'aws-cdk-lib/aws-lambda';
7 |
8 | interface LambdaCICDStackProps extends StackProps {
9 | stackName?: string,
10 | repository?: codecommit.IRepository;
11 | lambdaFunction?: lambda.Function;
12 | }
13 |
14 | // Stack to setup CI/CD for Lambda function
15 | export class LambdaCICDStack extends Stack {
16 | constructor(scope: Construct, id: string, props?: LambdaCICDStackProps) {
17 | super(scope, id, props);
18 |
19 | // Custom Policy for the CodeBuild
20 | const codeBuildPolicy = new iam.PolicyStatement()
21 |
22 | // Adding permissions to policy
23 | codeBuildPolicy.addActions( "lambda:UpdateFunctionCode", "lambda:ListFunctions", "lambda:GetFunction")
24 |
25 | // Adding permissions to allow all the resources
26 | codeBuildPolicy.addResources(`${props?.lambdaFunction?.functionArn}`);
27 |
28 | // CI/CD Pipleline
29 | new CodePipeline(this, `${props?.stackName}-pipeline`, {
30 | pipelineName: `${props?.stackName}-pipeline`,
31 | selfMutation: false,
32 | synth: new CodeBuildStep('Deployment', {
33 | input: CodePipelineSource.codeCommit(props?.repository!, 'main'),
34 | installCommands: ['npm i'],
35 | commands: ['npm run build',
36 | 'zip -r -j function.zip dist/*',
37 | `aws lambda update-function-code --function-name ${props?.lambdaFunction?.functionName} --zip-file fileb://function.zip`
38 | ],
39 | primaryOutputDirectory: '.',
40 | rolePolicyStatements: [codeBuildPolicy],
41 | }
42 | )
43 | });
44 | }
45 | }
--------------------------------------------------------------------------------
/backend/src/lambda-dynamdb-api-stack.ts:
--------------------------------------------------------------------------------
1 | import { Stack, StackProps, RemovalPolicy, CfnOutput, Duration } from 'aws-cdk-lib';
2 | import { Construct } from 'constructs';
3 | import * as lambda from 'aws-cdk-lib/aws-lambda';
4 | import { AttributeType, Table } from 'aws-cdk-lib/aws-dynamodb';
5 | import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway';
6 | import * as apigw from 'aws-cdk-lib/aws-apigateway';
7 | import { join } from 'path'
8 |
9 | interface LambdaDynamoDBApiProps extends StackProps {
10 | stackName?: string,
11 | dynamoDBTable?: string
12 | }
13 |
14 | // Stack to setup CI/CD for Lambda function
15 | export class LambdaDynamoDBApiStack extends Stack {
16 | public readonly lambdaFunction: lambda.Function;
17 | public readonly restAPI: apigw.LambdaRestApi;
18 | constructor(scope: Construct, id: string, props?: LambdaDynamoDBApiProps) {
19 | super(scope, id, props);
20 |
21 | // AWS DynamoDB Table
22 | const dynamoTable = new Table(this, `${props?.dynamoDBTable}`, {
23 | partitionKey: {
24 | name: 'userId',
25 | type: AttributeType.STRING
26 | },
27 | tableName: props?.dynamoDBTable,
28 | removalPolicy: RemovalPolicy.DESTROY, // NOT recommended for production code, default removal policy is RETAIN
29 | });
30 |
31 | // AWS Lambda Function to do operations on Charts Data
32 | this.lambdaFunction = new lambda.Function(this, `${this.stackName}-lambda`, {
33 | runtime: lambda.Runtime.NODEJS_16_X, // execution environment
34 | code: lambda.Code.fromAsset(join(__dirname, '../lambda-compiled-code')), // code loaded from "lambda" directory
35 | handler: 'index.handler', // file is "index", function is "handler"
36 | environment: { // Envirnment Variables
37 | TABLE_NAME: `${props?.dynamoDBTable}`
38 | },
39 | memorySize: 512,
40 | timeout: Duration.seconds(300)
41 | })
42 |
43 | // Rest API to expose Lambda Function
44 | this.restAPI = new RestApi(this, `${this.stackName}-Endpoint`, {
45 | restApiName: `${this.stackName}-api`,
46 | defaultCorsPreflightOptions: { // 👇 set up CORS
47 | allowMethods: ['OPTIONS', 'GET', 'POST'],
48 | allowOrigins: ['*'], // Just used as sample, should be replaced with frontend apps origins
49 | },
50 | });
51 |
52 | // REST Proxy resource on API Gateway
53 | const charts = this.restAPI.root.addResource('{proxy+}');
54 |
55 | // Method to forward all the request to Lambda function
56 | charts.addMethod('ANY', new LambdaIntegration(this.lambdaFunction));
57 |
58 | // Granting rights to Lambda function to perform read and write operations on DynamoDB
59 | dynamoTable.grantReadWriteData(this.lambdaFunction);
60 |
61 | // Exports GraphQL API URL Endpoint
62 | new CfnOutput(this, 'APIGatewayURL', {
63 | value: `https://${this.restAPI.restApiId}.execute-api.${this.region}.amazonaws.com/${this.restAPI.deploymentStage.stageName}`,
64 | description: 'API Gateway URL',
65 | exportName: 'apiGatewayURL',
66 | });
67 | }
68 | }
--------------------------------------------------------------------------------
/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2018",
4 | "module": "commonjs",
5 | "outDir": "lib",
6 | "lib": [
7 | "es2018"
8 | ],
9 | "declaration": true,
10 | "strict": true,
11 | "noImplicitAny": true,
12 | "esModuleInterop": true,
13 | "strictNullChecks": true,
14 | "noImplicitThis": true,
15 | "alwaysStrict": true,
16 | "noUnusedLocals": false,
17 | "noUnusedParameters": false,
18 | "noImplicitReturns": true,
19 | "noFallthroughCasesInSwitch": false,
20 | "inlineSourceMap": true,
21 | "inlineSources": true,
22 | "experimentalDecorators": true,
23 | "strictPropertyInitialization": false,
24 | "typeRoots": [
25 | "./node_modules/@types"
26 | ]
27 | },
28 | "exclude": [
29 | "node_modules",
30 | "cdk.out"
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/react-charts-data-app/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | amplify
4 |
--------------------------------------------------------------------------------
/react-charts-data-app/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "plugins": [
4 | "@typescript-eslint",
5 | "import"
6 | ],
7 | "parser": "@typescript-eslint/parser",
8 | "parserOptions": {
9 | "ecmaVersion": 2018,
10 | "sourceType": "module",
11 | "project": "./tsconfig.json"
12 | },
13 | "extends": [
14 | "plugin:import/typescript"
15 | ],
16 | "settings": {
17 | "import/parsers": {
18 | "@typescript-eslint/parser": [
19 | ".ts",
20 | ".tsx"
21 | ]
22 | },
23 | "import/resolver": {
24 | "node": {},
25 | "typescript": {
26 | "project": "./tsconfig.json",
27 | "alwaysTryTypes": true
28 | }
29 | }
30 | },
31 | "ignorePatterns": [
32 | "*.js",
33 | "*.d.ts",
34 | "node_modules/"
35 | ],
36 | "rules": {
37 | "indent": [
38 | "off"
39 | ],
40 | "@typescript-eslint/indent": [
41 | "error",
42 | 2
43 | ],
44 | "quotes": [
45 | "error",
46 | "single",
47 | {
48 | "avoidEscape": true
49 | }
50 | ],
51 | "comma-dangle": [
52 | "error",
53 | "never"
54 | ],
55 | "comma-spacing": [
56 | "error",
57 | {
58 | "before": false,
59 | "after": true
60 | }
61 | ],
62 | "no-multi-spaces": [
63 | "error",
64 | {
65 | "ignoreEOLComments": false
66 | }
67 | ],
68 | "prefer-const": ["error", {
69 | "destructuring": "any",
70 | "ignoreReadBeforeAssign": false
71 | }],
72 | "array-bracket-spacing": [
73 | "error",
74 | "never"
75 | ],
76 | "array-bracket-newline": [
77 | "error",
78 | "consistent"
79 | ],
80 | "object-curly-spacing": [
81 | "error",
82 | "always"
83 | ],
84 | "object-curly-newline": [
85 | "error",
86 | {
87 | "multiline": true,
88 | "consistent": true
89 | }
90 | ],
91 | "object-property-newline": [
92 | "error",
93 | {
94 | "allowAllPropertiesOnSameLine": true
95 | }
96 | ],
97 | "keyword-spacing": [
98 | "error"
99 | ],
100 | "brace-style": [
101 | "error",
102 | "1tbs",
103 | {
104 | "allowSingleLine": true
105 | }
106 | ],
107 | "space-before-blocks": [
108 | "error"
109 | ],
110 | "curly": [
111 | "error",
112 | "multi-line",
113 | "consistent"
114 | ],
115 | "@typescript-eslint/member-delimiter-style": [
116 | "error"
117 | ],
118 | "semi": [
119 | "error",
120 | "always"
121 | ],
122 | "max-len": [
123 | "error",
124 | {
125 | "code": 150,
126 | "ignoreUrls": true,
127 | "ignoreStrings": true,
128 | "ignoreTemplateLiterals": true,
129 | "ignoreComments": true,
130 | "ignoreRegExpLiterals": true
131 | }
132 | ],
133 | "quote-props": [
134 | "error",
135 | "consistent-as-needed"
136 | ],
137 | "@typescript-eslint/no-require-imports": [
138 | "error"
139 | ],
140 | "import/order": [
141 | "warn",
142 | {
143 | "groups": [
144 | "builtin",
145 | "external"
146 | ],
147 | "alphabetize": {
148 | "order": "asc",
149 | "caseInsensitive": true
150 | }
151 | }
152 | ],
153 | "no-duplicate-imports": [
154 | "error"
155 | ],
156 | "no-shadow": [
157 | "off"
158 | ],
159 | "@typescript-eslint/no-shadow": [
160 | "error"
161 | ],
162 | "key-spacing": [
163 | "error"
164 | ],
165 | "no-multiple-empty-lines": [
166 | "error"
167 | ],
168 | "@typescript-eslint/no-floating-promises": [
169 | "error"
170 | ],
171 | "no-return-await": [
172 | "off"
173 | ],
174 | "@typescript-eslint/return-await": [
175 | "error"
176 | ],
177 | "no-trailing-spaces": [
178 | "error"
179 | ],
180 | "dot-notation": [
181 | "error"
182 | ],
183 | "no-bitwise": [
184 | "error"
185 | ],
186 | "@typescript-eslint/member-ordering": [
187 | "error",
188 | {
189 | "default": [
190 | "public-static-field",
191 | "public-static-method",
192 | "protected-static-field",
193 | "protected-static-method",
194 | "private-static-field",
195 | "private-static-method",
196 | "field",
197 | "constructor",
198 | "method"
199 | ]
200 | }
201 | ]
202 | }
203 | }
--------------------------------------------------------------------------------
/react-charts-data-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | node_modules
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | #amplify-do-not-edit-begin
27 | amplify/\#current-cloud-backend
28 | amplify/.config/local-*
29 | amplify/logs
30 | amplify/mock-data
31 | amplify/mock-api-resources
32 | amplify/backend/amplify-meta.json
33 | amplify/backend/.temp
34 | build/
35 | dist/
36 | node_modules/
37 | aws-exports.js
38 | awsconfiguration.json
39 | amplifyconfiguration.json
40 | amplifyconfiguration.dart
41 | amplify-build-config.json
42 | amplify-gradle-config.json
43 | amplifytools.xcconfig
44 | .secret-*
45 | **.sample
46 | #amplify-do-not-edit-end
47 |
--------------------------------------------------------------------------------
/react-charts-data-app/README.md:
--------------------------------------------------------------------------------
1 | ### React Charts Data App Deployment
2 |
3 | React Charts Data App is the App containing filters for the Charts and is deployed on AWS using AWS Amplify.
4 |
5 | CI/CD pipeline is configured using AWS Amplify.
6 |
7 | #### Prerequisite
8 |
9 | Make sure we have Amplify CLI up and running, and for installation use the [Amplify Official Website](https://docs.amplify.aws/cli/start/install/)
10 |
11 | ## Deployment on AWS
12 |
13 | 
14 |
15 | ### Deployment of the above architecture on AWS
16 |
17 | #### Step 1: Clone Repository
18 |
19 | ```console
20 | git clone https://github.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks.git
21 | cd micro-frontend-using-polyglot-javascript-frameworks/react-charts-data-app
22 | ```
23 |
24 | #### Step 2: Install Dependencies
25 |
26 | ```console
27 | npm install
28 | ```
29 |
30 | ### Step 3: Initialize AWS Amplify
31 |
32 | ```console
33 | amplify init
34 | ```
35 |
36 | 
37 |
38 | ### Step 4: Add CI/CD and Deploy App
39 |
40 | ```console
41 | amplify add hosting
42 | ```
43 |
44 | Amplify hosting will ask a few questions, select the answers as present in the screenshot:
45 |
46 | 
47 |
48 | *After selecting the options, Amplify CLI would open Amplify console on the default web browser.*
49 |
50 | #### Step 4a: Setting up Hosting Environment
51 |
52 | Click on *Hosting environments* and Select *AWS CodeCommit*. Click "Connect Branch"
53 |
54 | 
55 |
56 | #### Step 4b: Setting up Repository
57 |
58 | Select *microfrontend-react-charts-data-app* repository from the drop down and select *main* branch.
59 |
60 | 
61 |
62 | #### Step 4c: Configure the Build Setting
63 |
64 | - Select *Environment* as **Dev** from the drop down
65 | - Select an existing service role or create a new one so Amplify Hosting may access your resources.
66 | - Click on *Advanced settings* drop down and set the environment variable **CHARTS_BACKEND_API** (Copy the value of ***APIGatewayURL***) *[(can get from Step 3 in the backend app deployment)](../backend/README.md)*)
67 |
68 | 
69 | 
70 |
71 | #### Step 4d: Review the CI/CD and Build Setting
72 |
73 | Review the setting and click on *Save and Deploy*
74 |
75 | 
76 |
77 | ### Step 5: Getting the React Charts Data App endpoint
78 |
79 | After completing *Step 4d*, come back to the existing terminal and hit enter. You can see the Frontend App Domain. Save this Domain in a text editor, we would be need with when deploying the React Container App.
80 |
81 | 
82 |
83 | [Lets' deploy our React Container App.](../react-container-app/README.md)
84 |
85 | [Go Back.](../README.md)
--------------------------------------------------------------------------------
/react-charts-data-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-charts-data-app",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@fortawesome/fontawesome-free": "^6.4.0",
7 | "html-webpack-plugin": "^5.5.0",
8 | "axios": "^1.4.0",
9 | "mdb-react-ui-kit": "^5.1.0",
10 | "react": "^18.2.0",
11 | "react-dom": "^18.2.0",
12 | "react-scripts": "5.0.1",
13 | "web-vitals": "^2.1.4"
14 | },
15 |
16 | "scripts": {
17 | "clean": "rm -rf node_modules build",
18 | "build": "npm run lint && webpack --mode production",
19 | "build:dev": "npm run lint && webpack --mode development",
20 | "lint": "npx eslint . --ext .ts,.tsx --fix",
21 | "build:start": "cd build && PORT=8081 npx serve",
22 | "start": "npm run lint && npx kill-port 8081 && webpack serve --mode development",
23 | "start:live": "npm run lint && npx kill-port 8081 && webpack serve --mode development --live-reload --hot"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "devDependencies": {
43 | "@typescript-eslint/eslint-plugin": "^5.57.1",
44 | "@typescript-eslint/parser": "^5.57.1",
45 | "brotli-webpack-plugin": "^1.1.0",
46 | "compression-webpack-plugin": "^10.0.0",
47 | "css-loader": "^6.7.3",
48 | "eslint": "^8.37.0",
49 | "eslint-plugin-import": "^2.27.5",
50 | "html-webpack-plugin": "^5.5.1",
51 | "kill-port": "^2.0.1",
52 | "react-dom": "^18.2.0",
53 | "style-loader": "^3.3.2",
54 | "ts-loader": "^9.4.2",
55 | "typescript": "^5.0.3",
56 | "webpack": "^5.81.0",
57 | "webpack-cli": "^5.0.2",
58 | "webpack-dev-server": "^4.13.3",
59 | "zlib": "^1.0.5"
60 | },
61 | "overrides": {
62 | "react-scripts": {
63 | "typescript": "^5"
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/react-charts-data-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | React App
10 |
11 |
12 |
13 | You need to enable JavaScript to run this app.
14 |
15 |
16 |
--------------------------------------------------------------------------------
/react-charts-data-app/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import 'mdb-react-ui-kit/dist/css/mdb.min.css';
4 | import '@fortawesome/fontawesome-free/css/all.min.css';
5 | import { ChartData } from './components/ChartData';
6 | import reportWebVitals from './reportWebVitals';
7 |
8 | const root = ReactDOM.createRoot(
9 | document.getElementById('root') as HTMLElement
10 | );
11 |
12 | root.render(
13 |
14 |
15 |
16 | );
17 |
18 | // If you want to start measuring performance in your app, pass a function
19 | // to log results (for example: reportWebVitals(console.log))
20 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
21 | reportWebVitals();
22 |
--------------------------------------------------------------------------------
/react-charts-data-app/src/components/ChartData.tsx:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import { MDBRow, MDBInputGroup, MDBTypography, MDBBtn } from 'mdb-react-ui-kit';
3 | import React from 'react';
4 |
5 | import './style.css';
6 |
7 | // ChartState interface
8 | interface ChartState {
9 | mobilePhones: number;
10 | cameras: number;
11 | laptops: number;
12 | chairs: number;
13 | tables: number;
14 | wardrobes: number;
15 | jeans: number;
16 | shirts: number;
17 | shoes: number;
18 | userId: string;
19 | }
20 |
21 | const backendAPI = process.env.CHARTS_BACKEND_API_URL || 'http://localhost:3000';
22 |
23 | // ChartData Props
24 | export type ChartDataProps = { user?: any };
25 |
26 | // ChartData Component
27 | export class ChartData extends React.Component {
28 | constructor(props: {user: any}) {
29 | super(props);
30 |
31 | this.state = {
32 | mobilePhones: 0,
33 | cameras: 0,
34 | laptops: 0,
35 | chairs: 0,
36 | tables: 0,
37 | wardrobes: 0,
38 | jeans: 0,
39 | shirts: 0,
40 | shoes: 0,
41 | userId: (this.props.user)? this.props.user.username : ''
42 | };
43 |
44 | this.handleChange = this.handleChange.bind(this);
45 | this.getChartsData = this.getChartsData.bind(this);
46 | this.saveData = this.saveData.bind(this);
47 | }
48 |
49 | // Get ChartData and update the state
50 | async getChartsData() {
51 |
52 | // Getting ChartData from API
53 | try {
54 | const res = await axios.get( `${backendAPI}/charts/${this.props.user.username}`);
55 | const { data, status } = await res;
56 | if (status === 200) {
57 | this.setState(data);
58 | }
59 | } catch (err) {
60 | console.log('Something went wrong!');
61 |
62 | }
63 | }
64 |
65 | // Persist chart data to the database using API
66 | async updateChartsData() {
67 | if (this.props.user) {
68 | await axios.post(`${backendAPI}/charts`, this.state);
69 | } else {
70 | alert('Not a vaild user!');
71 | }
72 | }
73 |
74 | // Called immediately after a component is mounted
75 | componentDidMount() {
76 | if (this.props.user) {
77 | this.getChartsData().then(() => {}).catch((err) => {});
78 | }
79 | }
80 |
81 | saveData(event) {
82 | // Creating Custom Event
83 | const chartUpdateEvent = new CustomEvent('CHARTS_UPDATE_EVENT', { detail: this.state });
84 |
85 | this.updateChartsData().then(() => {
86 | // Dispatching Event
87 | window.dispatchEvent(chartUpdateEvent);
88 | }).catch((err) => {});
89 |
90 | event.preventDefault();
91 | }
92 |
93 | // Trigged when change event is detected on the input box
94 | handleChange(event) {
95 | const { name, value } = event.target;
96 |
97 | const newChartState: ChartState = this.state;
98 | newChartState[name] = value;
99 |
100 | this.setState(newChartState);
101 | }
102 |
103 | // Renders the HTML code
104 | render() {
105 | return (
106 | <>
107 |
Product Categories Data
108 |
158 |
>
159 | );
160 | }
161 | }
--------------------------------------------------------------------------------
/react-charts-data-app/src/components/style.css:
--------------------------------------------------------------------------------
1 | .chartsDataAppForm {
2 | padding-right: 10%;
3 | padding-left: 10%;
4 | padding-top: 10%;
5 | }
6 |
7 | .chartsDataApp {
8 | padding-top: 5%;
9 | padding-bottom: 16%;
10 | background-color: rgb(237 242 247);;
11 | }
12 |
13 | .chartsDataAppText {
14 | padding-top: 3%;
15 | color: darkblue;
16 | }
17 |
18 | .submitButton {
19 | padding-top: 4%;
20 | }
--------------------------------------------------------------------------------
/react-charts-data-app/src/index.ts:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 | export {};
--------------------------------------------------------------------------------
/react-charts-data-app/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/react-charts-data-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "noImplicitAny": false,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "module": "esnext",
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "noEmit": false,
22 | "jsx": "react-jsx"
23 | },
24 | "include": [ "src" ]
25 | }
--------------------------------------------------------------------------------
/react-charts-data-app/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const {container, DefinePlugin} = require('webpack');
3 | const path = require('path');
4 | const zlib = require('zlib');
5 | const CompressionPlugin = require("compression-webpack-plugin");
6 | const deps = require("./package.json").dependencies;
7 |
8 | module.exports = {
9 | entry: './src/index',
10 | mode: 'development',
11 | devServer: {
12 | static: {
13 | directory: path.join(__dirname, 'build'),
14 | },
15 | port: 8081
16 | },
17 | output: {
18 | publicPath: 'auto',
19 | path: path.resolve(__dirname, 'build'),
20 | },
21 | optimization: {
22 | minimize: true,
23 | },
24 | module: {
25 | rules: [
26 | {
27 | test: /\.(jsx|js|tsx)$/,
28 | loader: 'ts-loader',
29 | exclude: /node_modules/,
30 | },
31 | {
32 | test: /\.(css|scss)$/,
33 | use: ["style-loader", "css-loader"],
34 | },
35 | {
36 | test: /\.(png|svg|jpg|jpeg|gif|ico)$/,
37 | exclude: /node_modules/,
38 | use: ['file-loader']
39 | }
40 | ]
41 | },
42 | resolve: {
43 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
44 | },
45 | plugins: [
46 | new container.ModuleFederationPlugin({
47 | name: 'reactChartsDataApp',
48 | filename: 'remoteEntry.js',
49 | exposes: {
50 | './ChartData': './src/components/ChartData',
51 | },
52 | shared: {
53 | 'react': {
54 | singleton: true,
55 | eager: true,
56 | requiredVersion: deps["react"]
57 | },
58 | 'react-dom': {
59 | singleton: true,
60 | eager: true,
61 | requiredVersion: deps["react-dom"]
62 | }
63 | },
64 | }),
65 | new HtmlWebpackPlugin({
66 | template: './public/index.html',
67 | }),
68 | new CompressionPlugin({
69 | filename: "[path][base].br",
70 | algorithm: "brotliCompress",
71 | test: /\.(js|css|html|svg)$/,
72 | compressionOptions: {
73 | params: {
74 | [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
75 | },
76 | },
77 | threshold: 10240,
78 | minRatio: 0.8,
79 | deleteOriginalAssets: false,
80 | }),
81 | new DefinePlugin({
82 | 'process.env.CHARTS_BACKEND_API_URL': JSON.stringify(`${process.env.CHARTS_BACKEND_API}`),
83 | })
84 | ],
85 | };
--------------------------------------------------------------------------------
/react-container-app/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | amplify
4 |
--------------------------------------------------------------------------------
/react-container-app/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "plugins": [
4 | "@typescript-eslint",
5 | "import"
6 | ],
7 | "parser": "@typescript-eslint/parser",
8 | "parserOptions": {
9 | "ecmaVersion": 2018,
10 | "sourceType": "module",
11 | "project": "./tsconfig.json"
12 | },
13 | "extends": [
14 | "plugin:import/typescript"
15 | ],
16 | "settings": {
17 | "import/parsers": {
18 | "@typescript-eslint/parser": [
19 | ".ts",
20 | ".tsx"
21 | ]
22 | },
23 | "import/resolver": {
24 | "node": {},
25 | "typescript": {
26 | "project": "./tsconfig.json",
27 | "alwaysTryTypes": true
28 | }
29 | }
30 | },
31 | "ignorePatterns": [
32 | "*.js",
33 | "*.d.ts",
34 | "node_modules/"
35 | ],
36 | "rules": {
37 | "indent": [
38 | "off"
39 | ],
40 | "@typescript-eslint/indent": [
41 | "error",
42 | 2
43 | ],
44 | "quotes": [
45 | "error",
46 | "single",
47 | {
48 | "avoidEscape": true
49 | }
50 | ],
51 | "comma-dangle": [
52 | "error",
53 | "never"
54 | ],
55 | "comma-spacing": [
56 | "error",
57 | {
58 | "before": false,
59 | "after": true
60 | }
61 | ],
62 | "no-multi-spaces": [
63 | "error",
64 | {
65 | "ignoreEOLComments": false
66 | }
67 | ],
68 | "prefer-const": ["error", {
69 | "destructuring": "any",
70 | "ignoreReadBeforeAssign": false
71 | }],
72 | "array-bracket-spacing": [
73 | "error",
74 | "never"
75 | ],
76 | "array-bracket-newline": [
77 | "error",
78 | "consistent"
79 | ],
80 | "object-curly-spacing": [
81 | "error",
82 | "always"
83 | ],
84 | "object-curly-newline": [
85 | "error",
86 | {
87 | "multiline": true,
88 | "consistent": true
89 | }
90 | ],
91 | "object-property-newline": [
92 | "error",
93 | {
94 | "allowAllPropertiesOnSameLine": true
95 | }
96 | ],
97 | "keyword-spacing": [
98 | "error"
99 | ],
100 | "brace-style": [
101 | "error",
102 | "1tbs",
103 | {
104 | "allowSingleLine": true
105 | }
106 | ],
107 | "space-before-blocks": [
108 | "error"
109 | ],
110 | "curly": [
111 | "error",
112 | "multi-line",
113 | "consistent"
114 | ],
115 | "@typescript-eslint/member-delimiter-style": [
116 | "error"
117 | ],
118 | "semi": [
119 | "error",
120 | "always"
121 | ],
122 | "max-len": [
123 | "error",
124 | {
125 | "code": 150,
126 | "ignoreUrls": true,
127 | "ignoreStrings": true,
128 | "ignoreTemplateLiterals": true,
129 | "ignoreComments": true,
130 | "ignoreRegExpLiterals": true
131 | }
132 | ],
133 | "quote-props": [
134 | "error",
135 | "consistent-as-needed"
136 | ],
137 | "@typescript-eslint/no-require-imports": [
138 | "error"
139 | ],
140 | "no-duplicate-imports": [
141 | "error"
142 | ],
143 | "no-shadow": [
144 | "off"
145 | ],
146 | "@typescript-eslint/no-shadow": [
147 | "error"
148 | ],
149 | "key-spacing": [
150 | "error"
151 | ],
152 | "no-multiple-empty-lines": [
153 | "error"
154 | ],
155 | "@typescript-eslint/no-floating-promises": [
156 | "error"
157 | ],
158 | "no-return-await": [
159 | "off"
160 | ],
161 | "@typescript-eslint/return-await": [
162 | "error"
163 | ],
164 | "no-trailing-spaces": [
165 | "error"
166 | ],
167 | "dot-notation": [
168 | "error"
169 | ],
170 | "no-bitwise": [
171 | "error"
172 | ],
173 | "@typescript-eslint/member-ordering": [
174 | "error",
175 | {
176 | "default": [
177 | "public-static-field",
178 | "public-static-method",
179 | "protected-static-field",
180 | "protected-static-method",
181 | "private-static-field",
182 | "private-static-method",
183 | "field",
184 | "constructor",
185 | "method"
186 | ]
187 | }
188 | ]
189 | }
190 | }
--------------------------------------------------------------------------------
/react-container-app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | node_modules
6 | /.pnp
7 | .pnp.js
8 |
9 | # testing
10 | /coverage
11 |
12 | # production
13 | /build
14 |
15 | # misc
16 | .DS_Store
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | #amplify-do-not-edit-begin
27 | amplify/\#current-cloud-backend
28 | amplify/.config/local-*
29 | amplify/logs
30 | amplify/mock-data
31 | amplify/mock-api-resources
32 | amplify/backend/amplify-meta.json
33 | amplify/backend/.temp
34 | build/
35 | dist/
36 | node_modules/
37 | aws-exports.js
38 | awsconfiguration.json
39 | amplifyconfiguration.json
40 | amplifyconfiguration.dart
41 | amplify-build-config.json
42 | amplify-gradle-config.json
43 | amplifytools.xcconfig
44 | .secret-*
45 | **.sample
46 | #amplify-do-not-edit-end
47 |
--------------------------------------------------------------------------------
/react-container-app/README.md:
--------------------------------------------------------------------------------
1 | ### React Container App Deployment
2 |
3 | React Container App is the App responsible for Micro-Frontend Apps integration. Both React and Angular apps are being imported as a remote and leveraged in the Container App.
4 |
5 | React Container App also uses AWS Cognito for user Authentication.
6 |
7 | AWS Amplify is leveraged hosting the app, configuring the CI/CD, and setting up AWS Cognito for user Authentication.
8 |
9 | #### Prerequisite
10 |
11 | - Make sure we have Amplify CLI up and running, and for installation use the [Amplify Official Website](https://docs.amplify.aws/cli/start/install/)
12 |
13 | ## Deployment on AWS
14 |
15 | 
16 |
17 | ### Deployment of the above architecture on AWS
18 |
19 | #### Step 1: Clone Repository
20 |
21 | ```console
22 | git clone https://github.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks.git
23 | cd micro-frontend-using-polyglot-javascript-frameworks/react-container-app
24 | ```
25 |
26 | #### Step 2: Install Dependencies
27 |
28 | ```console
29 | npm install
30 | ```
31 |
32 | ### Step 3: Initialize AWS Amplify
33 |
34 | ```console
35 | amplify init
36 | ```
37 |
38 | 
39 |
40 | ### Step 4: Add Authentication
41 |
42 | ```console
43 | amplify add auth
44 | ```
45 |
46 | Select the options as present in the screenshot
47 |
48 | 
49 |
50 | ### Step 5: Push local amplify changes to AWS
51 |
52 | Push the local changes to AWS, and Amplify would create resources needed for the Authentication (AWS Cognito, IAM Role, Lambda Function)
53 |
54 | ```console
55 | amplify push
56 | ```
57 |
58 | 
59 |
60 | ### Step 6: Add CI/CD and Deploy App
61 |
62 | ```console
63 | amplify add hosting
64 | ```
65 |
66 | Amplify hosting will ask a few questions, select the answers as present in the screenshot:
67 |
68 | 
69 |
70 | *After selecting the options, Amplify CLI would open Amplify console on the default web browser.*
71 |
72 | #### Step 6a: Setting up Hosting Environment
73 |
74 | Click on *Hosting environments* and Select *AWS CodeCommit*. Click "Connect Branch"
75 |
76 | 
77 |
78 | #### Step 6b: Setting up Repository
79 |
80 | Select *microfrontend-react-container-app* repository from the drop down and select *main* branch.
81 |
82 | 
83 |
84 | #### Step 6c: Configure the Build Setting
85 |
86 | - Select *Environment* as **Dev** from the drop down
87 | - Select an existing service role or create a new one so Amplify Hosting may access your resources.
88 | - Click on *Advanced settings* drop down and set a environment variable for the micro-frontend app **REACT_CHARTS_DATA_APP** (Copy the value of ***React Charts Data App Domain***) *[(Get from Step 5 in the React Charts Data APP deployment)](../react-charts-data-app/README.md)*)
89 | - set another environment variable for the Micro-frontend app **ANGULAR_CHARTS_APP** (Copy the value of ***Angular Charts App Domain***) *[(Get from Step 5 in the Angular Charts APP deployment)](../angular-charts-app/README.md)*)
90 |
91 | 
92 | 
93 |
94 | #### Step 6d: Review the CI/CD and Build Setting
95 |
96 | Review the setting and click on *Save and Deploy*
97 |
98 | 
99 |
100 | ### Step 7: Getting the React Container App endpoint
101 |
102 | After completing *Step 6d*, come back to the existing terminal and hit enter. You can see the Frontend App Domain.
103 |
104 | 
105 |
106 | Frontend Domain is the Container application, open it on the Browser and try out the Micro-Frontend App.
107 |
108 | 
109 |
110 | [Lets' go back.](../README.md)
--------------------------------------------------------------------------------
/react-container-app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-container-app",
3 | "version": "0.0.1",
4 | "private": true,
5 | "dependencies": {
6 | "@aws-amplify/ui-react": "^4.6.0",
7 | "@fortawesome/fontawesome-free": "^6.3.0",
8 | "aws-amplify": "^5.1.3",
9 | "html-webpack-plugin": "^5.5.0",
10 | "mdb-react-ui-kit": "^5.1.0",
11 | "react": "^18.2.0",
12 | "react-dom": "^18.2.0",
13 | "react-scripts": "5.0.1",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "clean": "rm -rf node_modules build",
18 | "build": "npm run lint && webpack --mode production",
19 | "lint": "npx eslint . --ext .ts,.tsx --fix",
20 | "build:dev": "npm run lint && webpack --mode development",
21 | "build:start": "npm run lint && cd build && PORT=8080 npx serve",
22 | "start": "npm run lint && npx kill-port 8080 && webpack serve --open --mode development",
23 | "start:live": "npm run lint && webpack serve --open --mode development --live-reload --hot"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "devDependencies": {
43 | "@typescript-eslint/eslint-plugin": "^5.57.1",
44 | "@typescript-eslint/parser": "^5.57.1",
45 | "compression-webpack-plugin": "^10.0.0",
46 | "copy-webpack-plugin": "^11.0.0",
47 | "eslint": "^8.37.0",
48 | "kill-port": "^2.0.1",
49 | "mini-css-extract-plugin": "^2.7.5",
50 | "ts-loader": "^9.4.2",
51 | "typescript": "^5.0.4",
52 | "webpack": "^5.76.1",
53 | "webpack-cli": "^5.0.1",
54 | "zlib": "^1.0.5"
55 | },
56 | "overrides": {
57 | "react-scripts": {
58 | "typescript": "^5"
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/react-container-app/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/react-container-app/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/react-container-app/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/react-container-app/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/favicon-16x16.png
--------------------------------------------------------------------------------
/react-container-app/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/favicon-32x32.png
--------------------------------------------------------------------------------
/react-container-app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/public/favicon.ico
--------------------------------------------------------------------------------
/react-container-app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Charts App
16 |
17 |
18 |
19 | You need to enable JavaScript to run this app.
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/react-container-app/src/assets/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-samples/micro-frontend-using-polyglot-javascript-frameworks/096dc429c4de70a8e00e1d8c6bc8e795bd82cc84/react-container-app/src/assets/app_logo.png
--------------------------------------------------------------------------------
/react-container-app/src/bootstrap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './components/app';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | const root = ReactDOM.createRoot(
8 | document.getElementById('root') as HTMLElement
9 | );
10 |
11 | root.render(
12 |
13 |
14 |
15 | );
16 |
17 | // If you want to start measuring performance in your app, pass a function
18 | // to log results (for example: reportWebVitals(console.log))
19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
20 | reportWebVitals();
21 |
--------------------------------------------------------------------------------
/react-container-app/src/components/app.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | AmplifyProvider,
3 | Authenticator,
4 | withAuthenticator
5 | } from '@aws-amplify/ui-react';
6 | import { Amplify } from 'aws-amplify';
7 | import React, { Suspense } from 'react';
8 |
9 | // Importing Components
10 | import awsconfig from './../aws-exports';
11 | import Container from './container/index';
12 | import Footer from './footer/footer';
13 | import Header from './header/header';
14 |
15 | Amplify.configure(awsconfig);
16 |
17 | class App extends React.Component {
18 | render() {
19 | return (
20 |
21 |
22 | {({ signOut, user }) => (
23 |
24 | {user && (
25 | <>
26 | loading...
}>
27 |
28 |
29 |
30 | >
31 | )}
32 |
33 | )}
34 |
35 |
36 | );
37 | }
38 | }
39 |
40 | export default withAuthenticator(App);
41 |
42 |
--------------------------------------------------------------------------------
/react-container-app/src/components/container/index.tsx:
--------------------------------------------------------------------------------
1 | import 'mdb-react-ui-kit/dist/css/mdb.min.css';
2 | import '@fortawesome/fontawesome-free/css/all.min.css';
3 | import { MDBContainer, MDBCol } from 'mdb-react-ui-kit';
4 | import '@aws-amplify/ui-react/styles.css';
5 | import React from 'react';
6 | import './style.css';
7 |
8 | /* ***************Importing Mircofronted App Component ********
9 | ************************** Lazy Load *****************************/
10 |
11 | //Importing React Charts Data App
12 | const CharDataApp = React.lazy(() => import('reactChartsDataApp/ChartData').then(({ ChartData }) => ({ default: ChartData })));
13 |
14 | //Importing Angular Charts App
15 | const ChartsApp = React.lazy(() => import('../../utils/external-angular-app'));
16 |
17 | export type ContainerProps = { user?: any };
18 |
19 | class Container extends React.Component {
20 | render() {
21 | return (
22 | <> @React Container App
23 |
24 |
25 |
26 |
27 | @React App
28 |
29 |
30 |
31 |
32 |
33 | @Angular App
34 |
35 |
36 |
37 |
38 | >
39 |
40 | );
41 | }
42 | }
43 |
44 | export default Container;
45 |
--------------------------------------------------------------------------------
/react-container-app/src/components/container/style.css:
--------------------------------------------------------------------------------
1 | .mainContainer {
2 | padding-top: 2%;
3 | padding-bottom: 3%;
4 | }
5 |
6 | .ContanierHeader {
7 | text-align: left;
8 | padding-left: 1%;
9 | text-size-adjust: 10%;
10 | font-size: 0.775em;
11 | }
12 |
13 | .reactChartsDataApp {
14 | padding-top: 10%;
15 | }
16 |
17 | .BMIChartsAppHeader {
18 | text-align: left;
19 | padding-left: 1%;
20 | text-size-adjust: 10%;
21 | font-size: 0.775em;
22 | }
23 |
24 | .BMIDataAppHeader {
25 | text-align: left;
26 | padding-left: 1%;
27 | text-size-adjust: 10%;
28 | font-size: 0.775em;
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/react-container-app/src/components/footer/footer.css:
--------------------------------------------------------------------------------
1 | .website-link {
2 | padding-left: 2px !important;
3 | }
4 |
5 | .footer {
6 | margin-bottom: -2%;
7 | }
8 |
--------------------------------------------------------------------------------
/react-container-app/src/components/footer/footer.tsx:
--------------------------------------------------------------------------------
1 | import { MDBFooter, MDBContainer, MDBIcon, MDBBtn } from 'mdb-react-ui-kit';
2 | import React from 'react';
3 | import 'mdb-react-ui-kit/dist/css/mdb.min.css';
4 | import '@fortawesome/fontawesome-free/css/all.min.css';
5 | import './footer.css';
6 |
7 | class Footer extends React.Component {
8 | render() {
9 | return (
10 |
11 |
12 |
13 |
21 |
22 |
23 |
24 |
32 |
33 |
34 |
35 |
43 |
44 |
45 |
53 |
54 |
55 |
56 |
64 |
65 |
66 |
67 |
75 |
76 |
77 |
78 |
84 |
85 |
86 |
87 |
88 | );
89 | }
90 | }
91 |
92 | export default Footer;
93 |
--------------------------------------------------------------------------------
/react-container-app/src/components/header/header.css:
--------------------------------------------------------------------------------
1 | .signout-button {
2 | background-color: #3b71ca;
3 | color: white;
4 | }
--------------------------------------------------------------------------------
/react-container-app/src/components/header/header.tsx:
--------------------------------------------------------------------------------
1 | import { Button } from '@aws-amplify/ui-react';
2 | import { MDBContainer, MDBNavbar, MDBNavbarBrand } from 'mdb-react-ui-kit';
3 | import React from 'react';
4 | import logo from '../../assets/app_logo.png';
5 | import './header.css';
6 |
7 | // Header Props for getting signOut function for Amplify Authentication
8 | export type HeaderProps = { signOut?: any; user?: any };
9 |
10 | // Header component for the application
11 | class Header extends React.Component {
12 |
13 | constructor(props: any) {
14 | super(props);
15 | }
16 |
17 | render() {
18 | return (
19 | <>
20 |
21 |
22 |
23 |
30 | Charts App
31 |
32 |
33 | Sign Out
34 |
35 |
36 |
37 | >
38 | );
39 | }
40 | }
41 |
42 | export default Header;
--------------------------------------------------------------------------------
/react-container-app/src/global.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'chartsApp/chartsApp' {
2 | export { mount } from 'chartsApp/chartsApp';
3 | }
4 |
5 | declare module 'reactChartsDataApp/ChartData' {
6 | export { ChartData } from 'reactChartsDataApp/ChartData';
7 | }
8 |
9 | declare module "*.png" {
10 | const value: any;
11 | export = value;
12 | }
--------------------------------------------------------------------------------
/react-container-app/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
--------------------------------------------------------------------------------
/react-container-app/src/index.ts:
--------------------------------------------------------------------------------
1 | import('./bootstrap');
2 | export {};
--------------------------------------------------------------------------------
/react-container-app/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/react-container-app/src/utils/external-angular-app.jsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react";
2 | import { mount } from "angularChartsApp/angularChartsApp";
3 |
4 | const AngularChartsAppModule = (userDataKey) => {
5 | const ref = useRef(null);
6 | useEffect(()=> {
7 | // Passing Cogito userDataKey to Angular app
8 | mount(userDataKey);
9 | }, []);
10 | return ;
11 | };
12 |
13 | export default AngularChartsAppModule;
--------------------------------------------------------------------------------
/react-container-app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "ESNext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "noImplicitAny": false,
13 | "allowSyntheticDefaultImports": true,
14 | "strict": true,
15 | "forceConsistentCasingInFileNames": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "module": "ESNext",
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "noEmit": false,
22 | "jsx": "react-jsx"
23 | },
24 | "include": [ "src" ]
25 | }
--------------------------------------------------------------------------------
/react-container-app/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const MiniCSSPlugin = require('mini-css-extract-plugin');
3 | const MiniCSSExtractPlugin = require('mini-css-extract-plugin');
4 | const CompressionPlugin = require('compression-webpack-plugin');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 | const { ModuleFederationPlugin } = require('webpack').container;
7 | const path = require('path');
8 | const zlib = require('zlib');
9 |
10 | let reactChartsDataApp = "reactChartsDataApp@http://localhost:8081/remoteEntry.js"
11 | let angularChartsaApp = "angularChartsApp@http://localhost:8082/remoteEntry.js"
12 |
13 | // If REACT_CHARTS_DATA_APP and ANGULAR_CHARTS_APP are exported use the following
14 | if (process.env.REACT_CHARTS_DATA_APP && process.env.ANGULAR_CHARTS_APP) {
15 | reactChartsDataApp = `reactChartsDataApp@${process.env.REACT_CHARTS_DATA_APP}/remoteEntry.js`
16 | angularChartsaApp = `angularChartsApp@${process.env.ANGULAR_CHARTS_APP}/remoteEntry.js`
17 | }
18 |
19 | module.exports = {
20 | entry: './src/index',
21 | mode: 'production',
22 | devServer: {
23 | static: {
24 | directory: path.join(__dirname, 'build'),
25 | },
26 | port: 8080,
27 | },
28 | output: {
29 | publicPath: 'auto',
30 | path: path.resolve(__dirname, 'build')
31 | },
32 | optimization: {
33 | minimize: true,
34 | },
35 | module: {
36 | rules: [
37 | {
38 | test: /\.(jsx|js|tsx)$/,
39 | loader: 'ts-loader',
40 | exclude: /node_modules/,
41 | },
42 | {
43 | test: /\.(css)$/,
44 | use: [MiniCSSPlugin.loader, "css-loader"],
45 | },
46 | {
47 | test: /\.(png|svg|jpg|jpeg|gif|ico)$/,
48 | exclude: /node_modules/,
49 | use: ['file-loader']
50 | }
51 | ],
52 | },
53 | resolve: {
54 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
55 | },
56 | plugins: [
57 | new ModuleFederationPlugin({
58 | remotes: {
59 | reactChartsDataApp: reactChartsDataApp,
60 | angularChartsApp: angularChartsaApp
61 | },
62 | }),
63 | new CopyWebpackPlugin({
64 | patterns: [
65 | {
66 | from: "*.png",
67 | to: "./",
68 | context: "public",
69 | },
70 | ],
71 | }),
72 | new MiniCSSPlugin({
73 | filename: 'style.css'
74 | }),
75 | new MiniCSSExtractPlugin({
76 | filename:'style.[contenthash].css'
77 | }),
78 | new HtmlWebpackPlugin({
79 | template: './public/index.html',
80 | }),
81 | new CompressionPlugin({
82 | filename: "[path][base].br",
83 | algorithm: "brotliCompress",
84 | test: /\.(js|css|html|svg)$/,
85 | compressionOptions: {
86 | params: {
87 | [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
88 | },
89 | },
90 | threshold: 10240,
91 | minRatio: 0.8,
92 | deleteOriginalAssets: false,
93 | })
94 | ],
95 | };
--------------------------------------------------------------------------------