Lifting and Shifting a Web Application to AWS Serverless
This post provides a guide on how to migrate a MERN (Mongo, Express React, and Node.js) web application to a serverless environment.
This article was authored by AWS Principal Developer Advocate, Marcia Villalba, and published with permission.
This article provides a guide on how to migrate a MERN (Mongo, Express, React, and Node.js) web application to a serverless environment. It not only looks at the process of migrating a non-serverless web application to a serverless environment, but it also explores two issues that arise during the migration process.
Lift and shift is often the fastest way to get a migrated application into production, and it allows the development team to refactor parts of the application that may benefit from serverless technologies.
Before starting any migration, it is important to define the non-functional requirements that the new application needs to have. For this application these requirements are:
- An environment that scales to zero
- Paying as little as possible for idle time
- Configuring as little infrastructure as possible
- Automatic high availability of the application
- Minimal changes to the original code
This blog post guides you on how to migrate a MERN application. The original application is hosted on two different servers: one contains the Mongo database and another contains the Node/js/Express and ReactJS applications.
This demo application simulates a swag e-commerce site. The database layer stores the products, users, and purchase history. The server layer takes care of the e-commerce business logic, hosting the product images, and user authentication and authorization. The web layer takes care of all the user interaction and communicates with the server layer using REST APIs.
The database layer of the application is migrated to MongoDB Atlas, a cloud-based database cluster that scales automatically and is paid for what is used. This case is as simple as dumping the content of the database in a local folder and then restoring it in the cloud.
The Node.js/Express backend is migrated to AWS Lambda using the AWS Lambda Web Adapter, an open-source project that allows one to build a web application and run it on Lambda. You can learn more about migration in this video.
The next step is to create an HTTP endpoint for the server application. This demo uses Lambda function URLs, as they are simple to configure, and one function URL forwards all routes to the Express server. You can learn more about Lambda Functions URLs in this video.
The React web app is migrated to AWS Amplify, a fully managed service that provides features like hosting web applications and managing the CI/CD pipeline for the web app. You can see how to do the migration by following this video.
Up to here, the application is migrated from being hosted in a traditional environment to running using the serverless infrastructure. However, during this migration, there are two issues that arise: authentication and authorization, and storage.
Authentication and Authorization Migration
The original application handles the authentication and the authorization by itself. However, with the current migrated application every time you log in, you are logged out unexpectedly from the application. This is because the server code is responsible for handling the authentication and the authorization of the users, and now our server is running in an AWS Lambda function and functions are stateless. This means that there will be one function running per request—a request can load all the products on the landing page, get the details for a product, or log in to the site—and if you do something in one of these functions, the state is not shared across.
To solve this, you must remove the authentication and authorization mechanisms from the function and use a service that can preserve the state across multiple invocations of the functions. That is why this migration uses Amazon Cognito to handle authentication and authorization. You can learn more about Amazon Cognito in this video.
With this new architecture, the application calls Amazon Cognito APIs directly from the AWS Amplify application, minimizing the amount of code needed. To learn how this part of the migration was done check this other video.
In the original application, when a new product is created, a new image is uploaded to the Node.js/Express server. However, now the application resides in a Lambda function. The code (and files) that are part of that function cannot change unless the function is redeployed. Consequently, you must separate the user storage from the server code.
For solving this issue this migration will use Amazon S3, an object storage service that provides scalability, data availability, security, and performance. Additionally, Amazon CloudFront can be used to accelerate the retrieval of images from the cloud. If you want to learn how this migration was done, you can check this video.
By following this guide, you can quickly and easily migrate your web application to a serverless environment and take advantage of the benefits of serverless, such as automatic scaling and paying for what is used.
This is a summary of the migration steps that this article suggests:
- Database migration: Migrate the database from on-premises to MongoDB Atlas.
- Backend migration: Migrate the NodeJS/Express application from on-premises to an AWS Lambda using Lambda Web Adapter and Lambda Function URLs.
- Web App migration: Migrate the React web app from on-premises to AWS Amplify.
- Authentication migration: Migrate the custom-built authentication to use Amazon Cognito.
- Storage migration: Migrate the local storage of images to use Amazon S3 and Amazon CloudFront.
The following image shows the proposed solution for the migrated application:
If you want to read an extended article on how to do this, why every service was picked over others, and access the code for the original and migrated application, you can read "Lifting and shifting a web application to AWS Serverless: Part 1," and if you are more a visual person, the video below covers it as well.