How to deploy NextJS on AWS, the same way Vercel does? - amazon-web-services

I want to deploy NextJS on AWS using AWS CDK for a POC and was looking at options. In the NextJS docs, it says that we can just create an instance and run npm run build && npm start, it will start the service for us. However, this is not the most optimised way of deploying.
Vercel deploys this in the most optimized way possible:
How can I do the same with AWS? How can I serve the static assets and pages via Cloudfront CDN and the server side rendered pages and APIs via either Lambda or ECS? Is there a step by step guide that I can follow to split out the build files for the same?
Other options I explored
AWS Amplify: As it is a premium service, I feel doing all this by my self would be a lot cheaper and gives me more flexibility in CDK (I am not sure how Amplify works behind the scenes to deploy the nextjs assets on a S3 + Cloudfront + Lambda stack)
serverless framework: There is a plugin to deploy nextjs. But, I want to have full control over the deployment and don't want to depend on any external framework. Want to do it natively using AWS CDK.
Any pointers to do this natively using AWS CDK would be helpful. Thanks.

Deploying Next.js as a serverless application requires a bunch of services when you don't want to pack the whole Next.js server into a single Lambda.
My current setup of AWS services to achieve this looks like the following:
It consists of 3 main resources:
CloudFront
This works as a serverless reverse proxy that routes the traffic from the Internet to S3 (JavaScript, prerendered pages) or Lambda (Server rendered pages).
When using the image optimization capabilities of Next.js you also need an extra service that provides the API for it.
S3
Since you don't want to invoke Lambdas just to serve static content, you need a S3 bucket where those files are stored and served from.
Lambda
The Lambdas are then used to serve the server generated pages (SSR & API).
They contain a minimal version of the Next.js server (e.g. without the static files that are served from S3).
I built this setup with Terraform, so there is no native CDK solution available at this time.
But most of it could be simply translated to CDK since the model behind Terraform and CDK is pretty much the same.
Source code of the Terraform module is available on GitHub: https://github.com/milliHQ/terraform-aws-next-js

Related

Access Denied Error and Improper Routing in Deployment of Tailwind Next.js Starter Template on AWS using Github Actions

Problem Statement:
The tailwind nextjs starter template is unable to be deployed properly on AWS using Github Actions. The deployment process involves pushing the export files to S3, and displaying them using S3 + Cloudfront + Route53.
One of my domains example: https://domainA.com works by just sharing this files to S3 without exporting them (Using github actions, I share this files to s3 and then connect it with cloudfront using Origin access identity. (It is working as expected)
but another one of my domains example: https://domainB.com doesn't work and gives access denied issue. (I checked bucket policy and it allows access to s3 bucket, bucket is publicly accessible)
I want to solve above error, please suggest options.
Now coming to another problem, As I have realized that the files in S3 should be output files and
so I now export this files to s3 locations using github actions. The cloudfront is connected to s3 bucket using OAI or public origin access. Once everything is setup correctly, I am able to route to my domain but it is unable to work properly. I am assuming that the system is unable to locate additional files from S3 that it needs.
how can I also solve above error.
Issue: "The tailwind nextjs starter template is unable to be deployed properly on AWS using Github Actions. The deployment process involves pushing the export files to S3, and displaying them using S3 + Cloudfront + Route53. The domain (https://domainB.com) gives an access denied issue despite the S3 bucket being publicly accessible and allowing access."
Solution:
The issue is because of the dynamic routing in Next.js with S3, Cloudfront and Route53. The export files are separated into individual files for each route, leading to the system being unable to locate additional files from S3.
To resolve this issue, there are several options that can be considered:
Amplify: AWS manages a service called Amplify, which is a CI/CD service that deploys Next.js apps to your AWS account.
Serverless Next.js (sls-next) Component: A Serverless Framework Component that allows you to deploy Next.js apps to your AWS account via Serverless Inc's deployment infrastructure. (I used this option)
SST Dev: A platform to deploy Next.js apps to your AWS account.
Using Terraform: An infrastructure as code tool that can be used to deploy Next.js apps to your AWS account.
By choosing one of the above options, you can effectively deploy your Next.js starter template on AWS using Github Actions.

Can I include Routes and Node_modules in an AWS S3 hosted website

I am looking at using AWS to host the website for my personal business. The website is simple and (currently) does not require a server. My website is written in React and does have some npm packages installed and also uses a router to navigate to other pages on my site.
Will an S3 bucket host a website that has routes and dependencies? What if my site grows and I eventually need a server. Will I be able to attach an EC2 instance to my site?
Option 1:
Take a look at AWS Amplfiy as it might manage everything for you in Option 2 and super easy to get started. I use this for several website. Usually only takes minutes to get working if you already have an AWS account.
In short, from the AWS Console you can connect your app from GitHub, BitBucket, GitLab, and AWS CodeCommit. It usually will auto detect the type of project you have.
Amplify will create a managed S3 bucket and setup CloudFront you. It also registers a webwook with GIT so whenever you commit on a specific branch will rebuild and publish your site. You can even have multiple branch so www.acme.com -> master and working.acme.com -> branch
Option 2:
S3 <--- CloudFront <-- Lambda#Edge <-- Browser
EC2 <--|
Store you file in S3
Use CloudFront (Content Delivery Network)
Create a Lambda#Edge to handle your routing.
Then in the future you can an EC2 server.
Look at the answer for the Lambda#Edge Multiple Cloudfront Origins with Behavior Path Redirection

AWS Amplify & Serverless-Stack

I am currently looking into AWS Amplify as well as I am reading Serverless Stack. My goal is to create a simple ToDo list app. Both "Getting started" / Documentations seem to have the same goal. However, AWS Amplify guide seems to be way easier from the setup.
And that's where I am confused. As far as I understand AWS Amplify also uses DynamoDB and gets data via GraphQL. But where is the difference between these two documentations?
Serverless Stack is a resource providing guidance on how to create serverless applications with AWS. It was created by a company called Anomaly Innovations.
AWS Amplify is an open source framework maintained by AWS which helps developers integrate their applications with AWS resources.
AWS Amplify is a very confusing service and consists of many components. I would categorize as follow.
AWS Amplify Console
AWS Amplify CLI
AWS SDK&Libraries to integrate to your mobile or web
AWS Appsync Transformer
AWS Amplify Console gives you the ability to easily to setup Continous Deployment for your Amplify project. Amplify Console use together with AWS Amplify CLI for you to manage different environments.
Let's say you want to start the Todo App. You start on your local using Amplify CLI and create API Gateway/Lambda/DynamoDB stacks.
Amplify CLI lets you create the whole stack easily and push it to AWS to deploy the whole stack. Then you can create a different environment based on the same stacks, let's say you want your dev environment, and QA environment and production environment.
Amplify CLI gives you all the commands necessary for you to achieve this, then if you want to auto-deploy the change to AWS when someone push the code to your Git repository, you can use the Amplify Console to set up exactly that.
Amplify Console also integrate with AWS Domain so, you can easily point your own domain to any of the environment.
On top of these, Amplify also provides, GraphQL Transformer, which you can easily define the GraphQL schema in Amplify format and it will transform and deploy to AWS Appsync. And there is a Mobile SDK which you can sync data between AppSync and you're mobile and provides some UIs as well.
We used one of our web projects and we liked it for Continues Deployment aspect of the Amplify, but we didn't like the AppSync(GraphQL) aspect of Amplify just b/c it was not easy to implement layered resolver.
Also, keep in mind that Amplify CLI/SDK/Transformer is under one project and it's still very fragile. You can take a look at the version history from https://www.npmjs.com/package/#aws-amplify/cli and you will see few version bump just in a single month. There were many obvious bugs we encounter, even on the AWS Console.
I haven't use the Serverless yet, but as long as I know, Serverless provides No1 and No2 of Amplify with greater stability.

How to deploy Webpack build to AWS from Bitbucket?

I have a React application that I am bundling using Webpack. The app relies on a MongoDB database and a Node/Express server to field the backend of the app (API requests, etc.).
I want to set up continuous integration/deployment (C.I/D.), but am not sure where to start. As my app's GIT repo is with Bitbucket and I have had experience with AWS in the past, it would be good to enable C.I/D. using these. How do I go about this?
You can use Jenkins to build your project from BitBucket.
Make use of AWS CodePipeline and AWS CodeDeploy for continuous delivery on AWS.
Jenkins gives you the flexibility to work with any source control system, and has plugins for AWS CodePipeline.
From AWS CodePipeline, you can configure a stage to call a Jenkins build job.
I've been using this system in production for quite some time now, without any issues.

zero downtime deployment for frontend resources

My stack is Wildfly, angular, spring, RDS, cloudfront. Frontend resources (html/js etc) are stored in app (ie delivered by Wildfly).
For backend and DB I can deploy with zero downtime with 2 EC2 behind ELB, but I am not sure how to handle this scenario:
User get old js/html from our server -> deployment of new version done -> user click on something which use old api (eg, the new version has a new mandatory param)
Is there a way to avoid this? I can only think of putting default value for the new param. Or would API versioning make sense here?
Another question: what if the frontend resources are delivered by cloudfront + s3? how to make the deployment of new resources to s3 in sync with backend?
I can only think of putting default value for the new param. Or would
API versioning make sense here?
This sounds like exactly what API versioning is intended to solve. You would change API versions anytime there is a change that would break clients of the previous version.
Another question: what if the frontend resources are delivered by
cloudfront + s3? how to make the deployment of new resources to s3 in
sync with backend?
Deploying them at the same time is up to you. That's part of your deployment process that you need to automate somehow. You can use versioning and order of deployment to help some here. For example, if your entire front-end is deployed on S3:
Deploy a new version of your API, under a new API version number
Deploy new static UI resources
Issue a CloudFront cache invalidation
Users start seeing new front-end resources that reference the new back-end API version
If your front-end UI is a mix of EC2 server dynamic resources and S3 static resources, and the EC2 UI components and the API are updated as part of the same deployment, then you can use a version prefix for your static resources on S3 to allow multiple versions to be available at once. For example:
Deploy new static UI resources to S3, with a new version prefix. This ensures that both the previous version(s) of the S3 resources and the new version are available at the same time.
Deploy the EC2 app, which updates both the EC2 UI components and the API
Users start loading the new version of the app from EC2, which references static resources under a new version prefix, which CloudFront then caches and serves
Obviously those are just a few scenarios and your situation probably differs in some way. In general you need to use versioning of any resources (static S3 resources, API resources, etc.) and a smart deployment order, to ensure that the end user doesn't see an interruption in service.