NextJS - Lambda & S3
< NextJS Overview >
Next.js provides 4 types of rendering methods:
1. Static Site Generation (SSG)
- generates static resources like HTML at build time
- The pre-rendered HTML is then reused on each request
- a good pre-rendering strategy for static content that rarely changes
- good for SEO
2. Server-Side Rendering (SSR)
- pre-rendering method that generates the HTML at request time
- when the application is already up and running
- good for pages that are dynamic
- also good for SEO since it is also a pre-render
3. Incremental Static Regeneration (ISR)
- good for apps with many pages where build times are very high if purely using SSG
- can build page per-page without needing to rebuild the entire app
4. Client-Side Rendering (CSR).
- the typical rendering strategy where the application is rendered in the browser with JavaScript.
< Building NextJS app >
When a Next.js application is built, Next.js transforms the application to production-optimized files.
- HTML for statically generated pages,
- JavaScript for rendering on the server,
- JavaScript for rendering on the client,
- and CSS files.
< Deploying NextJS App to S3 Bucket >
Static Assets
- Next.js uses the public directory under root to serve static assets such as images.
- these files are best stored in persistent storage and backed by a content delivery network (CDN)
- can add a prefix in the implementation to distinguish these static files.
- Static files not stored in the public directory are in .next/static.
- These static files are expected to be uploaded as _next/static to a CDN.
- No other code in the .next/directory should be uploaded to a CDN because that would expose server code.
Static Site Generation (SSG) pages
- For Static Site Generation, pages are rendered at build time and can be cached in CloudFront.
- Clients connect to a CloudFront distribution, which is configured to forward requests for static resources to S3
Dynamic pages
- API Gateway forwards requests to the Next.js app running on Lambda, which performs the server-side rendering.
- To create dynamic routes, add brackets to a page file, for example, pages/user/[id].js
- This creates a statically generated page with the path /user/[id] where [id] can be dynamic.
Output File Tracing
- At build time, Next.js Output File Tracing determines the minimal set of files needed for deploying to Lambda.
- The files are automatically copied to a standalone directory and must be enabled in next.config.js.
Github Actions
We can also create a github action yaml file to automatically upload build files to S3 everytime we push to main:
< Deploying NextJS App to S3 >
Problem: Routing Issue
- 404 Error when access "http://bucket-name.s3-website-ap-southeast-1.amazonaws.com/portfolios" or "/portfolios/1"
- But User can access if we append .html to the path, e.g ".../portfolios.html"
Solution: Using CloudFront & Lambda@Edge
Solution is to create a CloudFront distribution and a Lambda@Edge to front the request to correct the path
1. Create a Lambda Function at us-east-1
- We need to create the Lambda at us-east-1 as we are using it as Lambda@Edge.
- Lambda@Edge functions are distributed globally, but they originate from one place.
- The reason is most likely that there needs to be a single source of truth, and they picked us-east-1.
2. Add "edgelambda.amazonaws.com" to Lambda Exection Role:
Go to IAM and find the lambda role and edit Trust Relationship:
3. Create a CloudFront distribution
- After creating the Lambda, create a CloudFront Distribution
- Under CloudFront Disribution behaviour tab, associate the lambda at Origin Request with Lambda@Edge Type.
- Copy and paste the Lambda's ARN with the correct version.
< AWS Lambda Web Adapter [Github] >
- Since the Next.js application is essentially a webserver, need to use the AWS Lambda Web Adapter
- it serves as a Lambda layer to convert incoming events from API Gateway to HTTP requests that Next.js can process.
- Once processed, the AWS Lambda Web Adapter converts the HTTP response back to a Lambda event response.
- The Lambda handler is configured to run the minimal server.js file created by the standalone build step.
Sample Project
Go to the Sample Starter