Micro-frontends with AWS Amplify with CloudFront
In a previous story, we created a simple web application leveraging a micro-frontend architecture. Now it is time to deploy our app to the cloud. This story - Part 2 - is going to be “AWS-heavy”, as we deep dive into various AWS resources and services. The end result will be the deployment of the web app to AWS, which you can access from your machine.
Goal
We want to design and deploy below architecture by focusing on two areas:
- Deployments of all client micro-frontends to S3 and CloudFront.
- Login access via Amazon Cognito integration.
Excited? I am!
Prerequisites
In order to follow through, you should at least create or have access to a free AWS Account via the AWS Console. Basic AWS knowledge is recommended, thought I will keep things simple and give you some reference. As a rule of thumb, I recommend below basic security and cost-saving best practices:
- Once created a root user, create other users via IAM and switch to user account.
- Set up an alarm in AWS Budgets to get notified in case you get charged for some unexpected provisioning.
You should be ready now to start this journey. Now, before we can deploy our web-apps, let’s briefly introduce Amplify.
Introduction to AWS Amplify
AWS Amplify is a framework allowing to connect your web or mobile app to AWS Cloud resources, providing CLIs, libraries and UI components. Simply put, it creates AWS resources and links them with your project by exposing a set of services which can be configured (added) to your frontend application. Some examples are authentication (Cognito), hosting (S3 + CloudFront), api (REST or GraphQL) and much more.
We will rely heavily on CLI, which will ask you to answer some simple questions in order to generate the needed AWS resources to deploy your backend services. Do not worry, we will go through this together in the next section, where we will understand two very important Amplify commands, configure and init.
Amplify configure command
To get started with Amplify we need to install and configure the CLI on your local machine, with below one-time step commands:
npm i -g @aws-amplify/cli
amplify configure
Configure command links your local machine Amplify setup with your AWS account. You will be asked to sign-in in your AWS account and to create an IAM user. In our example we will name it amplify-micro-frontends-playground. See below animation for detailed steps:
Once IAM user is created, take notes of users’ access key and secret access key (store them carefully), add them to the prompt and proceed with setup. Among others, you need to create or reuse an existing local AWS profile. You can find it under your user directory in~/.aws/config and ~/.aws/credentials files. In my case I just reused my existing default profile. If everything went smooth, you should end up with similar screen:
Good stuff! Now we can start integrating Amplify to our micro-frontends web app by initializing it with real AWS resources.
Amplify init command
Now it is time to link your frontend application with AWS Amplify in order to provision resources where we will deploy the app:
cd mfe-parent
amplify init
The command will ask you a few questions aiming at linking your current project with an Amplify configuration, if in doubt consult documentation. You should end up with a screen as below:
Now that command got executed, let’s understand what has just happened:
- A new Amplify app, which contains an overview of all Amplify backends we are going to install, has been created in AWS Console.
- A newly deployed CloudFormation stack got created. Cloud Formation is a AWS service enabling infrastructure as code, the process of managing and automate the provision of AWS resources by defining them through a template and foster software development best practices in the operational/devops ecosystem, e.g. code versioning and pull requests. In our case, Amplify generated a CloudFormation stack (a collection of AWS resources manageable as a single unit), which provisioned an S3 deployment bucket containing Amplify metadata files (well explained here) and a couple of IAM roles (Auth & Unauth) which will be used later once we start adding AWS resources to our amplify configuration.
- A new amplify folder got created under frontend project. This includes a number of project metadata that glue your frontend configuration with Amplify remove configuration. Just a note about the backend-config.json file, which contains a description of all the resources you have created and installed with Amplify. You can deep dive here to details of all files.
- Last but not least. Notice we mentioned the package.json’s build script. This is important as Amplify will use that command to deploy the static content to another S3 when we will publish changes in CloudFront (coming soon).
At this point we have set foundation for our frontend project. Let’s now achieve our goal by deployment the micro-frontends to the cloud and protecting the app with a login screen.
Deploy micro-frontends
It is now finally time to deploy our micro-frontends. Typically, the de-facto place in AWS to deploy static content as images, JavaScript files and HTML pages is AWS Simple Cloud Storage (S3) (happy 15th anniversary!), a scalable object storage service. Moreover, to optimize applications performance and security, a common pattern is to set a Content Delivery Network in front of S3. Of course, AWS has the perfect partner in crime for that, which is Amazon CloudFront. It is not difficult to set this small architecture manually, but it may be time consuming for front end teams, or generally teams not familiar with cloud and/or AWS. Also we want to avoid repetition and have teams focusing on business logic, not on deploy pipeline every time they code. That is how Amplify comes in help: to boost deployment time and easiness of usage. Let’s assume we, as Core Team, need to deploy our own independent module to AWS. To do that, within the mfe-parent module, let’s execute the following command:
amplify add hosting
A lot happened behind the scenes. I chose the deployment to be based on S3 and CloudFront using HTTPS, because it mimics a real production setup. You should get a final result as below:
You will also notice that a new folder was created with the name hosting under the amplify/backend folder. It contains a CloudFormation template which basically instructs AWS to provision an S3 bucket which can be only accessed through CloudFront distribution, by creating a CloudFront Origin Access Identity (OAI). This is beautiful, as we are leveraging infrastructure as code to make this process consistent, automatic, with reduced error-rate across teams and pipelines. As this is fully automated and handled by Amplify, we just now to publish those changes:
amplify publish
This is pretty cool. Apart from provisioning S3 buckets and CloudFront distributions, Amplify has built your project (using the npm run build script provided during amplify init command) and added the generated bundle and static data into S3. Below picture gives a bit of overview on how S3, CloudFront and its integrations (OAI and IAM) are now available.
Repeat for accounts and payments
Now repeat both the init, hosting and publish commands for both accounts and payments module. If everything is in place, you should end up with:
- 6 S3 buckets (3 Amplify deployments + 3 buckets containing files from build process)
- 3 CloudFront distributions.
- 3 Amplify projects
Login feature
Now it is time to protect our web app by introducing a login screen. To do that, we will use Amplify to leverage Amazon Cognito User Pools. Let’s execute below command inside mfe-parent micro-frontend.
amplify add auth
The command generated a CloudFormation template under the amplify/auth folder, which, as done with previous hosting command, glues your local Amplify project with real AWS resources. Authentication with Amplify is based on Cognito User Pools, a user directory which allows us to create users who can sign in to our app. Let’s publish those changes to the cloud.
amplify publish
A lot of resources have been allocated via CloudFormation template. Below generated AWS resources make sure that your local Amplify profile is linked and authorized to access Cognito user and identity pools which relies on lambda functions and IAM roles and policies to do that.
Now that all AWS resources have been provisioned — and for the scope of our proof of concept — we can create any users who can access the web app. As shown below, let’s create guest /awsguest credentials which you can use later to access the web app.
Next question is how does user log in to our app. For that, we need to introduce a login screen to the React parent/shell app. We can achieve this by installing a React UI component made available by AWS amplify React library package. To do so, install the package as shown below:
npm install aws-amplify @aws-amplify/ui-react — save
Now we need to modify our App.js of mfe-parent app in order to use the component. We will leverage an Amplify UI Component for React, which exposes a set of components to mimic a login screen, as it is a pretty common scenario. The most minimal usage is to reuse the withAuthentication high order component to wrap the App and get an off-shelf implementation of a login, as shown in this article. In my case, I played a bit more both to learn its internals but also because I wanted to disable the sign up button which comes from default behavior of withAuthentication. Thus, my implementation looks like below:
Above snippet uses Amplify UI Components to display a login screen, without the sign in option. We hooked into the onAuthUIStateChange method in order to know whether we have signed in or not. In successful case, we show our content’s app.
If you now start the parent app, you should see a login screen like this:
Finally, to move your code to the S3 and CloudFront distribution, just publish:
amplify publish
One last step is to update your index.html file to reference the Cloudfront urls instead of your localhost bundles. You can find those urls on CloudFront distribution configurations.
We are done! Our micro-frontends architecture is now deployed on the cloud.
Summary
In Part 1 we demonstrated how is possible to leverage Web Components to create a micro-frontend web architecture based on Angular and React apps. Part 2 focused on deploying our app via Amplify, which, as demonstrated above, abstracts quite a bit of heavy-lifting internals and resource provisioning from frontend developers and allow them to deploy their app in minutes. Topic is big and this story just shows off a glimpse of all capabilities and creativity that AWS continuously fosters. There is always room for improvements and demo about:
- Automate all the above with Cloud Development Kit.
- Assess how teams interact with Amplify configurations and get more comfortable with.
- Streamline pipeline deployments in this area (AWS CodeCommit, CodeBuild and CodeDeploy or Jenkins)
- Don’t cheat with create-react-app and use Webpack
- Reuse a single CloudFront distribution instead of two.
- and much more
Hope this story gave you a sense of how above topics could be relevant in your organization and provoked your mind to try things out ;)
You can access the live demo here (guest/awsguest credentials), and I strongly suggest to go through this tutorial, try it your self, learn and have fun. Code can be found on Github. Thanks for the read and see you in next stories!
In Part 3 we will create a GraphQL based microfrontend.
AWS Gotchas
- If you publish frontend changes to S3, they may not reflect to CloudFront immediately as default TTL is 24h. You should then Invalidate the S3 objects or Use object versioning.
- You can share Amplify infrastructure with pull command.