├── nextjs-lambda-sam ├── run.sh ├── .eslintrc.json ├── public │ └── static │ │ └── favicon.ico ├── pages │ ├── _app.js │ ├── 404.js │ ├── api │ │ └── hello.js │ ├── posts │ │ └── [id].js │ └── index.js ├── next.config.js ├── Makefile ├── components │ ├── date.js │ ├── layout.module.css │ ├── error-layout.js │ └── layout.js ├── styles │ ├── globals.css │ ├── utils.module.css │ └── Home.module.css ├── package.json ├── posts │ ├── pre-rendering.md │ └── ssg-ssr.md ├── .gitignore ├── lib │ └── posts.js ├── README.md └── template.yaml ├── NOTICE ├── nextjs-lambda-cdk ├── app │ ├── .eslintrc.json │ ├── public │ │ └── static │ │ │ └── favicon.ico │ ├── pages │ │ ├── _app.js │ │ ├── 404.js │ │ ├── api │ │ │ └── hello.js │ │ ├── posts │ │ │ └── [id].js │ │ └── index.js │ ├── next.config.js │ ├── components │ │ ├── date.js │ │ ├── layout.module.css │ │ ├── error-layout.js │ │ └── layout.js │ ├── .gitignore │ ├── styles │ │ ├── globals.css │ │ ├── utils.module.css │ │ └── Home.module.css │ ├── package.json │ ├── posts │ │ ├── pre-rendering.md │ │ └── ssg-ssr.md │ ├── README.md │ └── lib │ │ └── posts.js ├── .npmignore ├── jest.config.js ├── bin │ └── nextjs-lambda-cdk.ts ├── .gitignore ├── test │ └── nextjs-lambda-cdk.test.ts ├── package.json ├── tsconfig.json ├── cdk.json ├── README.md └── lib │ └── nextjs-lambda-cdk-stack.ts ├── CHANGELOG.md ├── nextjs-serverless-architecture.png ├── .gitignore ├── CODE_OF_CONDUCT.md ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── README.md └── CONTRIBUTING.md /nextjs-lambda-sam/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | node server.js -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## Version 1.0.0 - November, 9 2022 4 | Initial release of sample project. 5 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /nextjs-serverless-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-nextjs/HEAD/nextjs-serverless-architecture.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # misc 4 | .DS_Store 5 | *.pem 6 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/public/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-nextjs/HEAD/nextjs-lambda-sam/public/static/favicon.ico -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/public/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-lambda-nextjs/HEAD/nextjs-lambda-cdk/app/public/static/favicon.ico -------------------------------------------------------------------------------- /nextjs-lambda-sam/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | const App = ({ Component, pageProps }) => { 4 | return 5 | } 6 | 7 | export default App 8 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '../styles/globals.css' 2 | 3 | const App = ({ Component, pageProps }) => { 4 | return 5 | } 6 | 7 | export default App 8 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | output: 'standalone', 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | output: 'standalone', 5 | } 6 | 7 | module.exports = nextConfig 8 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/bin/nextjs-lambda-cdk.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import * as cdk from 'aws-cdk-lib'; 3 | import { NextjsLambdaCdkStack } from '../lib/nextjs-lambda-cdk-stack'; 4 | 5 | const app = new cdk.App(); 6 | new NextjsLambdaCdkStack(app, 'NextjsLambdaCdkStack'); 7 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | npm install 3 | 4 | build: 5 | npm run build 6 | 7 | artifacts: 8 | # Copy artifacts for deployment 9 | cp -r .next/standalone/* $(ARTIFACTS_DIR) 10 | cp run.sh $(ARTIFACTS_DIR) 11 | 12 | build-NextFunction: install build artifacts -------------------------------------------------------------------------------- /nextjs-lambda-sam/components/date.js: -------------------------------------------------------------------------------- 1 | import { parseISO, format } from 'date-fns'; 2 | 3 | const Date = ({ dateString }) => { 4 | const date = parseISO(dateString); 5 | return ; 6 | } 7 | 8 | export default Date; 9 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/pages/404.js: -------------------------------------------------------------------------------- 1 | import ErrorLayout from '../components/error-layout'; 2 | 3 | const Custom404 = () => { 4 | return ( 5 | 6 |

404 - Page Not Found

7 |
8 | ); 9 | } 10 | 11 | export default Custom404; 12 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/components/date.js: -------------------------------------------------------------------------------- 1 | import { parseISO, format } from 'date-fns'; 2 | 3 | const Date = ({ dateString }) => { 4 | const date = parseISO(dateString); 5 | return ; 6 | } 7 | 8 | export default Date; 9 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/pages/404.js: -------------------------------------------------------------------------------- 1 | import ErrorLayout from '../components/error-layout'; 2 | 3 | const Custom404 = () => { 4 | return ( 5 | 6 |

404 - Page Not Found

7 |
8 | ); 9 | } 10 | 11 | export default Custom404; 12 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/components/layout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 36rem; 3 | padding: 0 1rem; 4 | margin: 3rem auto 6rem; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | .backToHome { 14 | margin: 3rem 0 0; 15 | } 16 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/components/layout.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 36rem; 3 | padding: 0 1rem; 4 | margin: 3rem auto 6rem; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | .backToHome { 14 | margin: 3rem 0 0; 15 | } 16 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/.gitignore: -------------------------------------------------------------------------------- 1 | # TS CDK projects ignore all .js files created by CDK except jest.config.js 2 | *.js 3 | !jest.config.js 4 | 5 | # Non CDK .js files need to be tracked 6 | !app/**.js 7 | !app/**/*.js 8 | 9 | *.d.ts 10 | node_modules 11 | package-lock.json 12 | 13 | # CDK asset staging directory 14 | .cdk.staging 15 | cdk.out 16 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | const handler = (req, res) => { 2 | res.status(200).json({ 3 | text: 'Hello', 4 | region: process.env.AWS_REGION, 5 | runtime: process.env.AWS_EXECUTION_ENV, 6 | memory: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE, 7 | timezone: process.env.TZ, 8 | date: Date(), 9 | }) 10 | } 11 | 12 | export default handler; 13 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/pages/api/hello.js: -------------------------------------------------------------------------------- 1 | const handler = (req, res) => { 2 | res.status(200).json({ 3 | text: 'Hello', 4 | region: process.env.AWS_REGION, 5 | runtime: process.env.AWS_EXECUTION_ENV, 6 | memory: process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE, 7 | timezone: process.env.TZ, 8 | date: Date(), 9 | }) 10 | } 11 | 12 | export default handler; 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Issue #, if available:** 2 | 3 | **Description of changes:** 4 | 5 | **Testing** 6 | 7 | 1. How did you test these changes? 8 | 2. Can these changes be tested using one of the demo application? If yes, which demo application can be used to test it? 9 | 3. If applicable, have you run `npm run build` successfully locally to fix all warnings and errors? 10 | 11 | By submitting this pull request, I confirm that my contribution is made under the terms of the MIT-0 license. 12 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, 6 | Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | line-height: 1.6; 8 | font-size: 18px; 9 | } 10 | 11 | * { 12 | box-sizing: border-box; 13 | } 14 | 15 | a { 16 | color: #0070f3; 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | img { 25 | max-width: 100%; 26 | display: block; 27 | } 28 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/styles/globals.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, 6 | Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; 7 | line-height: 1.6; 8 | font-size: 18px; 9 | } 10 | 11 | * { 12 | box-sizing: border-box; 13 | } 14 | 15 | a { 16 | color: #0070f3; 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | img { 25 | max-width: 100%; 26 | display: block; 27 | } 28 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-lambda", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "date-fns": "^2.29.1", 13 | "gray-matter": "^4.0.3", 14 | "next": "12.2.0", 15 | "react": "18.2.0", 16 | "react-dom": "18.2.0", 17 | "remark": "^14.0.2", 18 | "remark-html": "^15.0.1" 19 | }, 20 | "devDependencies": { 21 | "eslint": "8.18.0", 22 | "eslint-config-next": "12.2.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/test/nextjs-lambda-cdk.test.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Template, Match } from 'aws-cdk-lib/assertions'; 3 | import * as NextjsLambdaCdk from '../lib/nextjs-lambda-cdk-stack'; 4 | 5 | test('SQS Queue and SNS Topic Created', () => { 6 | // Arrange 7 | const app = new cdk.App(); 8 | 9 | // Act 10 | const stack = new NextjsLambdaCdk.NextjsLambdaCdkStack(app, 'MyTestStack'); 11 | 12 | // Assert 13 | const template = Template.fromStack(stack); 14 | 15 | template.resourceCountIs('AWS::S3::Bucket', 1); 16 | template.resourceCountIs('AWS::CloudFront::Distribution', 1); 17 | }); 18 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "postbuild": "echo \"#!/bin/bash\n\nnode server.js\" > .next/standalone/run.sh", 9 | "start": "next start", 10 | "lint": "next lint" 11 | }, 12 | "dependencies": { 13 | "date-fns": "^2.29.1", 14 | "gray-matter": "^4.0.3", 15 | "next": "12.2.0", 16 | "react": "18.2.0", 17 | "react-dom": "18.2.0", 18 | "remark": "^14.0.2", 19 | "remark-html": "^15.0.1" 20 | }, 21 | "devDependencies": { 22 | "eslint": "8.18.0", 23 | "eslint-config-next": "12.2.0" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-lambda-cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "nextjs-lambda-cdk": "bin/nextjs-lambda-cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "aws-cdk": "2.30.0", 15 | "@types/jest": "^27.5.2", 16 | "@types/node": "10.17.27", 17 | "@types/prettier": "2.6.0", 18 | "jest": "^27.5.1", 19 | "ts-jest": "^27.1.4", 20 | "ts-node": "^10.8.1", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "aws-cdk-lib": "2.30.0", 25 | "constructs": "^10.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/posts/pre-rendering.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Two Forms of Pre-rendering' 3 | date: '2020-01-01' 4 | --- 5 | 6 | Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. 7 | 8 | - **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request. 9 | - **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**. 10 | 11 | Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others. 12 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/posts/pre-rendering.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Two Forms of Pre-rendering' 3 | date: '2020-01-01' 4 | --- 5 | 6 | Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. 7 | 8 | - **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request. 9 | - **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**. 10 | 11 | Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others. 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Request a new feature 4 | title: '' 5 | labels: [feature-request, needs-triage] 6 | assignees: [] 7 | --- 8 | 9 | **Describe the feature you'd like** 10 | A clear and concise description of the feature you are proposing. 11 | 12 | **Proposed Solution** 13 | Suggest how to implement the addition or change. Please include prototype/workaround/sketch/reference implementation. 14 | 15 | **Other** 16 | Any alternative solutions or features you considered, a more detailed explanation, stack traces, related issues, links for context, etc. 17 | 18 | **Acknowledgements 19 | * [ ] I may be able to implement this feature request 20 | * [ ] This feature might incur a breaking change 21 | 22 | --- 23 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2018" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/.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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | node_modules 32 | package-lock.json 33 | 34 | ### SAM ### 35 | # Ignore build directories for the AWS Serverless Application Model (SAM) 36 | # Info: https://aws.amazon.com/serverless/sam/ 37 | # Docs: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-reference.html 38 | **/.aws-sam 39 | samconfig.toml 40 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/styles/utils.module.css: -------------------------------------------------------------------------------- 1 | .heading2Xl { 2 | font-size: 2.5rem; 3 | line-height: 1.2; 4 | font-weight: 800; 5 | letter-spacing: -0.05rem; 6 | margin: 1rem 0; 7 | } 8 | 9 | .headingXl { 10 | font-size: 2rem; 11 | line-height: 1.3; 12 | font-weight: 800; 13 | letter-spacing: -0.05rem; 14 | margin: 1rem 0; 15 | } 16 | 17 | .headingLg { 18 | font-size: 1.5rem; 19 | line-height: 1.4; 20 | margin: 1rem 0; 21 | } 22 | 23 | .headingMd { 24 | font-size: 1.2rem; 25 | line-height: 1.5; 26 | } 27 | 28 | .borderCircle { 29 | border-radius: 9999px; 30 | } 31 | 32 | .colorInherit { 33 | color: inherit; 34 | } 35 | 36 | .padding1px { 37 | padding-top: 1px; 38 | } 39 | 40 | .list { 41 | list-style: none; 42 | padding: 0; 43 | margin: 0; 44 | } 45 | 46 | .listItem { 47 | margin: 0 0 1.25rem; 48 | } 49 | 50 | .lightText { 51 | color: #666; 52 | } 53 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/styles/utils.module.css: -------------------------------------------------------------------------------- 1 | .heading2Xl { 2 | font-size: 2.5rem; 3 | line-height: 1.2; 4 | font-weight: 800; 5 | letter-spacing: -0.05rem; 6 | margin: 1rem 0; 7 | } 8 | 9 | .headingXl { 10 | font-size: 2rem; 11 | line-height: 1.3; 12 | font-weight: 800; 13 | letter-spacing: -0.05rem; 14 | margin: 1rem 0; 15 | } 16 | 17 | .headingLg { 18 | font-size: 1.5rem; 19 | line-height: 1.4; 20 | margin: 1rem 0; 21 | } 22 | 23 | .headingMd { 24 | font-size: 1.2rem; 25 | line-height: 1.5; 26 | } 27 | 28 | .borderCircle { 29 | border-radius: 9999px; 30 | } 31 | 32 | .colorInherit { 33 | color: inherit; 34 | } 35 | 36 | .padding1px { 37 | padding-top: 1px; 38 | } 39 | 40 | .list { 41 | list-style: none; 42 | padding: 0; 43 | margin: 0; 44 | } 45 | 46 | .listItem { 47 | margin: 0 0 1.25rem; 48 | } 49 | 50 | .lightText { 51 | color: #666; 52 | } 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/posts/ssg-ssr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'When to Use Static Generation v.s. Server-side Rendering' 3 | date: '2020-01-02' 4 | --- 5 | 6 | We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request. 7 | 8 | You can use Static Generation for many types of pages, including: 9 | 10 | - Marketing pages 11 | - Blog posts 12 | - E-commerce product listings 13 | - Help and documentation 14 | 15 | You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation. 16 | 17 | On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request. 18 | 19 | In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data. 20 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/posts/ssg-ssr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'When to Use Static Generation v.s. Server-side Rendering' 3 | date: '2020-01-02' 4 | --- 5 | 6 | We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request. 7 | 8 | You can use Static Generation for many types of pages, including: 9 | 10 | - Marketing pages 11 | - Blog posts 12 | - E-commerce product listings 13 | - Help and documentation 14 | 15 | You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation. 16 | 17 | On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request. 18 | 19 | In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Running Next.js applications with Serverless services on AWS 2 | 3 | This example walks you through how to run a Next.js application on Serverless services on AWS. There is a sample AWS Cloud Development Kit (CDK) application and AWS Serverless Application Model (SAM) application. Either CDK or SAM create a demo environment showing how you can use Amazon API Gateway, AWS Lambda, Amazon CloudFront, and Amazon S3 to run a Next.js application. 4 | 5 | ## Solution Architecture 6 | 7 | ![Next.js Serverless Architecture](nextjs-serverless-architecture.png) 8 | 9 | ## Requirements 10 | 11 | - Node.js 16.x 12 | - [AWS Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter) 13 | - AWS CDK 2.30.x 14 | - AWS SAM CLI 1.53.x 15 | - Configured AWS credentials 16 | 17 | ## Folder structure 18 | 19 | - /nextjs-lambda-cdk - Next.js application deployed with AWS CDK 20 | - /nextjs-lambda-sam - Next.js application deployed with AWS SAM 21 | 22 | ## Security 23 | 24 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 25 | 26 | ## License 27 | 28 | This sample code is licensed under the MIT-0 License. See the LICENSE file. 29 | 30 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/pages/posts/[id].js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | import Date from '../../components/date'; 4 | import Layout from '../../components/layout'; 5 | import { getAllPostIds, getPostData } from '../../lib/posts'; 6 | import utilStyles from '../../styles/utils.module.css'; 7 | 8 | export const getStaticProps = async ({ params }) => { 9 | const postData = await getPostData(params.id); 10 | return { 11 | props: { 12 | postData, 13 | }, 14 | }; 15 | } 16 | 17 | export const getStaticPaths = async () => { 18 | const paths = getAllPostIds(); 19 | return { 20 | paths, 21 | fallback: false, 22 | }; 23 | } 24 | 25 | const Post = ({ postData }) => { 26 | return ( 27 | 28 | 29 | {postData.title} 30 | 31 |
32 |

{postData.title}

33 |
34 | 35 |
36 |
37 |
38 |
39 | ); 40 | } 41 | 42 | export default Post; 43 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/pages/posts/[id].js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | 3 | import Date from '../../components/date'; 4 | import Layout from '../../components/layout'; 5 | import { getAllPostIds, getPostData } from '../../lib/posts'; 6 | import utilStyles from '../../styles/utils.module.css'; 7 | 8 | export const getStaticProps = async ({ params }) => { 9 | const postData = await getPostData(params.id); 10 | return { 11 | props: { 12 | postData, 13 | }, 14 | }; 15 | } 16 | 17 | export const getStaticPaths = async () => { 18 | const paths = getAllPostIds(); 19 | return { 20 | paths, 21 | fallback: false, 22 | }; 23 | } 24 | 25 | const Post = ({ postData }) => { 26 | return ( 27 | 28 | 29 | {postData.title} 30 | 31 |
32 |

{postData.title}

33 |
34 | 35 |
36 |
37 |
38 |
39 | ); 40 | } 41 | 42 | export default Post; 43 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/nextjs-lambda-cdk.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 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/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/core:target-partitions": [ 33 | "aws", 34 | "aws-cn" 35 | ] 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/components/error-layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | 4 | import styles from './layout.module.css'; 5 | import utilStyles from '../styles/utils.module.css'; 6 | 7 | export const siteTitle = 'Next.js Sample Website'; 8 | 9 | const ErrorLayout = ({ children }) => { 10 | return ( 11 |
12 | 13 | 14 | 18 | 24 | 25 | 26 | 27 |
{children}
28 |
29 | 30 | ← Back to home 31 | 32 |
33 |
34 | ) 35 | } 36 | 37 | export default ErrorLayout; 38 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/components/error-layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Link from 'next/link'; 3 | 4 | import styles from './layout.module.css'; 5 | import utilStyles from '../styles/utils.module.css'; 6 | 7 | export const siteTitle = 'Next.js Sample Website'; 8 | 9 | const ErrorLayout = ({ children }) => { 10 | return ( 11 |
12 | 13 | 14 | 18 | 24 | 25 | 26 | 27 |
{children}
28 |
29 | 30 | ← Back to home 31 | 32 |
33 |
34 | ) 35 | } 36 | 37 | export default ErrorLayout; 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug 4 | title: '' 5 | labels: [bug, needs-triage] 6 | assignees: [] 7 | --- 8 | 9 | **Demo** 10 | Specify the name of the demo that you encountered the bug. 11 | 12 | **Describe the bug** 13 | What is the problem? A clear and concise description of the bug. 14 | 15 | **Expected behavior** 16 | A clear and concise description of what you expected to happen. 17 | 18 | **Current behavior** 19 | What actually happened? 20 | 21 | Please include full errors, uncaught exceptions, stack traces, and relevant logs. 22 | 23 | **To Reproduce** 24 | Provide a self-contained, concise snippet of code that can be used to reproduce the issue. 25 | For more complex issues provide a repo with the smallest sample that reproduces the bug. 26 | 27 | Avoid including business logic or unrelated code, it makes diagnosis more difficult. 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. iOS] 31 | - Browser [e.g. chrome, safari] 32 | - Version [e.g. 22] 33 | 34 | **Smartphone (please complete the following information):** 35 | - Device: [e.g. iPhone6] 36 | - OS: [e.g. iOS8.1] 37 | - Browser [e.g. stock browser, safari] 38 | - Version [e.g. 22] 39 | 40 | **Additional context** 41 | Add any other context about the problem here. 42 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | ## Static Files 29 | 30 | In order to support `public` directory being deployed to S3 bucket and CloudFront, must use `/static` prefix for assets inside the `public` directory. 31 | 32 | ## Build 33 | 34 | ```bash 35 | npm run build 36 | ``` -------------------------------------------------------------------------------- /nextjs-lambda-sam/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Link from 'next/link'; 3 | 4 | import Date from '../components/date'; 5 | import Layout, { siteTitle } from '../components/layout'; 6 | import { getSortedPostsData } from '../lib/posts'; 7 | import utilStyles from '../styles/utils.module.css'; 8 | 9 | export const getStaticProps = async () => { 10 | const allPostsData = getSortedPostsData(); 11 | return { 12 | props: { 13 | allPostsData, 14 | }, 15 | }; 16 | } 17 | 18 | const Home = ({ allPostsData }) => { 19 | return ( 20 | 21 | 22 | {siteTitle} 23 | 24 |
25 |

Next.js First App showcasing SSG & SSR

26 |

27 | (This is a sample website - you’ll be building a site like this on{' '} 28 | our Next.js tutorial.) 29 |

30 |
31 | 32 |
33 |

Blog

34 |
    35 | {allPostsData.map(({ id, date, title }) => ( 36 |
  • 37 | 38 | {title} 39 | 40 |
    41 | 42 | 43 | 44 |
  • 45 | ))} 46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default Home; 53 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/pages/index.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head' 2 | import Link from 'next/link'; 3 | 4 | import Date from '../components/date'; 5 | import Layout, { siteTitle } from '../components/layout'; 6 | import { getSortedPostsData } from '../lib/posts'; 7 | import utilStyles from '../styles/utils.module.css'; 8 | 9 | export const getStaticProps = async () => { 10 | const allPostsData = getSortedPostsData(); 11 | return { 12 | props: { 13 | allPostsData, 14 | }, 15 | }; 16 | } 17 | 18 | const Home = ({ allPostsData }) => { 19 | return ( 20 | 21 | 22 | {siteTitle} 23 | 24 |
25 |

Next.js First App showcasing SSG & SSR

26 |

27 | (This is a sample website - you’ll be building a site like this on{' '} 28 | our Next.js tutorial.) 29 |

30 |
31 | 32 |
33 |

Blog

34 |
    35 | {allPostsData.map(({ id, date, title }) => ( 36 |
  • 37 | 38 | {title} 39 | 40 |
    41 | 42 | 43 | 44 |
  • 45 | ))} 46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default Home; 53 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Deploy on AWS 4 | 5 | 1. Clone git repository and navigate to CDK project 6 | 7 | ```bash 8 | git clone https://github.com/aws-samples/aws-lambda-nextjs.git 9 | cd nextjs-lambda-cdk 10 | ``` 11 | 12 | 2. Install CDK 13 | 14 | ```bash 15 | npm install 16 | 17 | ``` 18 | 19 | 3. Install and build the Next application in standalone mode (see `app/next.config.js`) 20 | 21 | ```bash 22 | cd app 23 | npm install 24 | npm run build 25 | ``` 26 | 27 | 4. Now navigate back to root of the CDK project and run CDK commands 28 | 29 | ```bash 30 | cd .. 31 | cdk bootstrap 32 | cdk synth 33 | ``` 34 | 35 | 5. Deploy the application 36 | 37 | ```bash 38 | cdk deploy 39 | ``` 40 | 41 | ## Next.js Static Files Support 42 | 43 | In order to support `public` directory being deployed to S3 bucket and CloudFront, must use `/static` prefix for assets inside the `public` directory. 44 | 45 | ## CDK TypeScript project 46 | 47 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 48 | 49 | ## Useful commands 50 | 51 | * `npm run build` compile typescript to js 52 | * `npm run watch` watch for changes and compile 53 | * `npm run test` perform the jest unit tests 54 | * `cdk deploy` deploy this stack to your default AWS account/region 55 | * `cdk diff` compare deployed stack with current state 56 | * `cdk synth` emits the synthesized CloudFormation template 57 | 58 | ## Destroy CDK app resources 59 | 60 | To clean up your CDK app run the below command: 61 | ```bash 62 | cdk destroy --all 63 | ``` 64 | 65 | Please be aware that some resources aren't automatically deleted and either need a retention policy that allows deletes or you need to delete them manually in you AWS account. 66 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | padding: 0 2rem; 3 | } 4 | 5 | .main { 6 | min-height: 100vh; 7 | padding: 4rem 0; 8 | flex: 1; 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | } 14 | 15 | .footer { 16 | display: flex; 17 | flex: 1; 18 | padding: 2rem 0; 19 | border-top: 1px solid #eaeaea; 20 | justify-content: center; 21 | align-items: center; 22 | } 23 | 24 | .footer a { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | flex-grow: 1; 29 | } 30 | 31 | .title a { 32 | color: #0070f3; 33 | text-decoration: none; 34 | } 35 | 36 | .title a:hover, 37 | .title a:focus, 38 | .title a:active { 39 | text-decoration: underline; 40 | } 41 | 42 | .title { 43 | margin: 0; 44 | line-height: 1.15; 45 | font-size: 4rem; 46 | } 47 | 48 | .title, 49 | .description { 50 | text-align: center; 51 | } 52 | 53 | .description { 54 | margin: 4rem 0; 55 | line-height: 1.5; 56 | font-size: 1.5rem; 57 | } 58 | 59 | .code { 60 | background: #fafafa; 61 | border-radius: 5px; 62 | padding: 0.75rem; 63 | font-size: 1.1rem; 64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, 65 | Bitstream Vera Sans Mono, Courier New, monospace; 66 | } 67 | 68 | .grid { 69 | display: flex; 70 | align-items: center; 71 | justify-content: center; 72 | flex-wrap: wrap; 73 | max-width: 800px; 74 | } 75 | 76 | .card { 77 | margin: 1rem; 78 | padding: 1.5rem; 79 | text-align: left; 80 | color: inherit; 81 | text-decoration: none; 82 | border: 1px solid #eaeaea; 83 | border-radius: 10px; 84 | transition: color 0.15s ease, border-color 0.15s ease; 85 | max-width: 300px; 86 | } 87 | 88 | .card:hover, 89 | .card:focus, 90 | .card:active { 91 | color: #0070f3; 92 | border-color: #0070f3; 93 | } 94 | 95 | .card h2 { 96 | margin: 0 0 1rem 0; 97 | font-size: 1.5rem; 98 | } 99 | 100 | .card p { 101 | margin: 0; 102 | font-size: 1.25rem; 103 | line-height: 1.5; 104 | } 105 | 106 | .logo { 107 | height: 1em; 108 | margin-left: 0.5rem; 109 | } 110 | 111 | @media (max-width: 600px) { 112 | .grid { 113 | width: 100%; 114 | flex-direction: column; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/lib/posts.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import matter from 'gray-matter'; 4 | import { remark } from 'remark'; 5 | import html from 'remark-html'; 6 | 7 | const postsDirectory = path.join(process.cwd(), 'posts'); 8 | 9 | export const getSortedPostsData = () => { 10 | // Get file names under /posts 11 | const fileNames = fs.readdirSync(postsDirectory); 12 | const allPostsData = fileNames.map((fileName) => { 13 | // Remove ".md" from file name to get id 14 | const id = fileName.replace(/\.md$/, ''); 15 | 16 | // Read markdown file as string 17 | const fullPath = path.join(postsDirectory, fileName); 18 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 19 | 20 | // Use gray-matter to parse the post metadata section 21 | const matterResult = matter(fileContents); 22 | 23 | // Combine the data with the id 24 | return { 25 | id, 26 | ...matterResult.data, 27 | }; 28 | }); 29 | 30 | // Sort posts by date 31 | return allPostsData.sort(({ date: a }, { date: b }) => { 32 | if (a < b) { 33 | return 1; 34 | } else if (a > b) { 35 | return -1; 36 | } else { 37 | return 0; 38 | } 39 | }); 40 | } 41 | 42 | export const getAllPostIds = () =>{ 43 | const fileNames = fs.readdirSync(postsDirectory); 44 | 45 | // Returns an array that looks like this: 46 | // [ 47 | // { 48 | // params: { 49 | // id: 'ssg-ssr' 50 | // } 51 | // }, 52 | // { 53 | // params: { 54 | // id: 'pre-rendering' 55 | // } 56 | // } 57 | // ] 58 | return fileNames.map((fileName) => { 59 | return { 60 | params: { 61 | id: fileName.replace(/\.md$/, ''), 62 | }, 63 | }; 64 | }); 65 | } 66 | 67 | export const getPostData = async (id) => { 68 | const fullPath = path.join(postsDirectory, `${id}.md`); 69 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 70 | 71 | // Use gray-matter to parse the post metadata section 72 | const matterResult = matter(fileContents); 73 | 74 | // Use remark to convert markdown into HTML string 75 | const processedContent = await remark() 76 | .use(html) 77 | .process(matterResult.content); 78 | const contentHtml = processedContent.toString(); 79 | 80 | // Combine the data with the id and contentHtml 81 | return { 82 | id, 83 | contentHtml, 84 | ...matterResult.data, 85 | }; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/lib/posts.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import matter from 'gray-matter'; 4 | import { remark } from 'remark'; 5 | import html from 'remark-html'; 6 | 7 | const postsDirectory = path.join(process.cwd(), 'posts'); 8 | 9 | export const getSortedPostsData = () => { 10 | // Get file names under /posts 11 | const fileNames = fs.readdirSync(postsDirectory); 12 | const allPostsData = fileNames.map((fileName) => { 13 | // Remove ".md" from file name to get id 14 | const id = fileName.replace(/\.md$/, ''); 15 | 16 | // Read markdown file as string 17 | const fullPath = path.join(postsDirectory, fileName); 18 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 19 | 20 | // Use gray-matter to parse the post metadata section 21 | const matterResult = matter(fileContents); 22 | 23 | // Combine the data with the id 24 | return { 25 | id, 26 | ...matterResult.data, 27 | }; 28 | }); 29 | 30 | // Sort posts by date 31 | return allPostsData.sort(({ date: a }, { date: b }) => { 32 | if (a < b) { 33 | return 1; 34 | } else if (a > b) { 35 | return -1; 36 | } else { 37 | return 0; 38 | } 39 | }); 40 | } 41 | 42 | export const getAllPostIds = () =>{ 43 | const fileNames = fs.readdirSync(postsDirectory); 44 | 45 | // Returns an array that looks like this: 46 | // [ 47 | // { 48 | // params: { 49 | // id: 'ssg-ssr' 50 | // } 51 | // }, 52 | // { 53 | // params: { 54 | // id: 'pre-rendering' 55 | // } 56 | // } 57 | // ] 58 | return fileNames.map((fileName) => { 59 | return { 60 | params: { 61 | id: fileName.replace(/\.md$/, ''), 62 | }, 63 | }; 64 | }); 65 | } 66 | 67 | export const getPostData = async (id) => { 68 | const fullPath = path.join(postsDirectory, `${id}.md`); 69 | const fileContents = fs.readFileSync(fullPath, 'utf8'); 70 | 71 | // Use gray-matter to parse the post metadata section 72 | const matterResult = matter(fileContents); 73 | 74 | // Use remark to convert markdown into HTML string 75 | const processedContent = await remark() 76 | .use(html) 77 | .process(matterResult.content); 78 | const contentHtml = processedContent.toString(); 79 | 80 | // Combine the data with the id and contentHtml 81 | return { 82 | id, 83 | contentHtml, 84 | ...matterResult.data, 85 | }; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/app/components/layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Image from 'next/image'; 3 | import Link from 'next/link'; 4 | 5 | import styles from './layout.module.css'; 6 | import utilStyles from '../styles/utils.module.css'; 7 | 8 | const name = 'Next.js applications with Serverless services on AWS'; 9 | export const siteTitle = 'Next.js Sample Website'; 10 | 11 | const Layout = ({ children, home }) => { 12 | return ( 13 |
14 | 15 | 16 | 20 | 26 | 27 | 28 | 29 |
30 | {home ? ( 31 | <> 32 | {name} 40 |

{name}

41 | 42 | ) : ( 43 | <> 44 | 45 | 46 | {name} 54 | 55 | 56 |

57 | 58 | {name} 59 | 60 |

61 | 62 | )} 63 |
64 |
{children}
65 | {!home && ( 66 |
67 | 68 | ← Back to home 69 | 70 |
71 | )} 72 |
73 | ) 74 | } 75 | 76 | export default Layout; 77 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/components/layout.js: -------------------------------------------------------------------------------- 1 | import Head from 'next/head'; 2 | import Image from 'next/image'; 3 | import Link from 'next/link'; 4 | 5 | import styles from './layout.module.css'; 6 | import utilStyles from '../styles/utils.module.css'; 7 | 8 | const name = 'Next.js applications with Serverless services on AWS'; 9 | export const siteTitle = 'Next.js Sample Website'; 10 | 11 | const Layout = ({ children, home }) => { 12 | return ( 13 |
14 | 15 | 16 | 20 | 26 | 27 | 28 | 29 |
30 | {home ? ( 31 | <> 32 | {name} 40 |

{name}

41 | 42 | ) : ( 43 | <> 44 | 45 | 46 | {name} 54 | 55 | 56 |

57 | 58 | {name} 59 | 60 |

61 | 62 | )} 63 |
64 |
{children}
65 | {!home && ( 66 |
67 | 68 | ← Back to home 69 | 70 |
71 | )} 72 |
73 | ) 74 | } 75 | 76 | export default Layout; 77 | -------------------------------------------------------------------------------- /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 | 61 | -------------------------------------------------------------------------------- /nextjs-lambda-cdk/lib/nextjs-lambda-cdk-stack.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CfnOutput, 3 | Duration, 4 | Stack, 5 | StackProps, 6 | } from 'aws-cdk-lib'; 7 | import { Construct } from 'constructs'; 8 | 9 | import * as apiGateway from 'aws-cdk-lib/aws-apigateway'; 10 | import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; 11 | import * as lambda from 'aws-cdk-lib/aws-lambda'; 12 | import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'; 13 | import * as s3 from 'aws-cdk-lib/aws-s3'; 14 | import * as s3deploy from 'aws-cdk-lib/aws-s3-deployment'; 15 | 16 | const path = require('node:path'); 17 | 18 | export class NextjsLambdaCdkStack extends Stack { 19 | constructor(scope: Construct, id: string, props?: StackProps) { 20 | super(scope, id, props); 21 | 22 | const lambdaAdapterLayer = lambda.LayerVersion.fromLayerVersionArn( 23 | this, 24 | 'LambdaAdapterLayerX86', 25 | `arn:aws:lambda:${this.region}:753240598075:layer:LambdaAdapterLayerX86:3` 26 | ); 27 | 28 | const nextCdkFunction = new lambda.Function(this, 'NextCdkFunction', { 29 | runtime: lambda.Runtime.NODEJS_16_X, 30 | handler: 'run.sh', 31 | code: lambda.Code.fromAsset(path.join( 32 | __dirname, 33 | '../app/.next/', 34 | 'standalone') 35 | ), 36 | architecture: lambda.Architecture.X86_64, 37 | environment: { 38 | 'AWS_LAMBDA_EXEC_WRAPPER': '/opt/bootstrap', 39 | 'RUST_LOG': 'info', 40 | 'PORT': '8080', 41 | }, 42 | layers: [lambdaAdapterLayer], 43 | }); 44 | 45 | const api = new apiGateway.RestApi(this, "api", { 46 | defaultCorsPreflightOptions: { 47 | allowOrigins: apiGateway.Cors.ALL_ORIGINS, 48 | allowMethods: apiGateway.Cors.ALL_METHODS 49 | } 50 | }); 51 | 52 | const nextCdkFunctionIntegration = new apiGateway.LambdaIntegration( 53 | nextCdkFunction, 54 | { 55 | allowTestInvoke: false 56 | } 57 | ); 58 | api.root.addMethod('ANY', nextCdkFunctionIntegration); 59 | 60 | api.root.addProxy({ 61 | defaultIntegration: new apiGateway.LambdaIntegration(nextCdkFunction, { 62 | allowTestInvoke: false 63 | }), 64 | anyMethod: true, 65 | }); 66 | 67 | const nextLoggingBucket = new s3.Bucket(this, 'next-logging-bucket', { 68 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 69 | encryption: s3.BucketEncryption.S3_MANAGED, 70 | versioned: true, 71 | accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE, 72 | }); 73 | 74 | const nextBucket = new s3.Bucket(this, 'next-bucket', { 75 | blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL, 76 | encryption: s3.BucketEncryption.S3_MANAGED, 77 | versioned: true, 78 | serverAccessLogsBucket: nextLoggingBucket, 79 | serverAccessLogsPrefix: 's3-access-logs', 80 | }); 81 | 82 | new CfnOutput(this, 'Next bucket', { value: nextBucket.bucketName }); 83 | 84 | const cloudfrontDistribution = new cloudfront.Distribution(this, 'Distribution', { 85 | defaultBehavior: { 86 | origin: new origins.RestApiOrigin(api), 87 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, 88 | cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED, 89 | }, 90 | additionalBehaviors: { 91 | '_next/static/*': { 92 | origin: new origins.S3Origin(nextBucket), 93 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY, 94 | }, 95 | 'static/*': { 96 | origin: new origins.S3Origin(nextBucket), 97 | viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.HTTPS_ONLY, 98 | }, 99 | }, 100 | minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2018, 101 | logBucket: nextLoggingBucket, 102 | logFilePrefix: 'cloudfront-access-logs', 103 | }); 104 | 105 | new CfnOutput(this, 'CloudFront URL', { 106 | value: `https://${cloudfrontDistribution.distributionDomainName}` 107 | }); 108 | 109 | new s3deploy.BucketDeployment(this, 'deploy-next-static-bucket', { 110 | sources: [s3deploy.Source.asset('app/.next/static/')], 111 | destinationBucket: nextBucket, 112 | destinationKeyPrefix: '_next/static', 113 | distribution: cloudfrontDistribution, 114 | distributionPaths: ['/_next/static/*'] 115 | } 116 | ); 117 | 118 | new s3deploy.BucketDeployment(this, 'deploy-next-public-bucket', { 119 | sources: [s3deploy.Source.asset('app/public/static/')], 120 | destinationBucket: nextBucket, 121 | destinationKeyPrefix: 'static', 122 | distribution: cloudfrontDistribution, 123 | distributionPaths: ['/static/*'] 124 | } 125 | ); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/README.md: -------------------------------------------------------------------------------- 1 | # Next.js SAM 2 | 3 | This project contains source code and supporting files for a serverless application that you can deploy with the SAM CLI. It includes the following files and folders. 4 | 5 | - nextjs-lambda-sam - Next.js Sample Application. 6 | - [template.yaml](template.yaml) - A template that defines the application's AWS resources. 7 | 8 | The application uses several AWS resources, including CloudFront, Lambda, API Gateway, and S3. These resources are defined in the [template.yaml](template.yaml) file in this project. You can update the template to add AWS resources through the same deployment process that updates your application code. 9 | 10 | ## Deploy the sample application 11 | 12 | The Serverless Application Model Command Line Interface (SAM CLI) is an extension of the AWS CLI that adds functionality for building and testing Lambda applications. It uses Docker to run your functions in an Amazon Linux environment that matches Lambda. It can also emulate your application's build environment and API. 13 | 14 | To use the SAM CLI, you need the following tools. 15 | 16 | * SAM CLI - [Install the SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) 17 | * Node.js - [Install Node.js 16](https://nodejs.org/en/), including the NPM package management tool. 18 | * Docker - [Install Docker community edition](https://hub.docker.com/search/?type=edition&offering=community) 19 | 20 | To build and deploy your application for the first time, run the following in your shell: 21 | 22 | ```bash 23 | sam build 24 | sam deploy --guided 25 | ``` 26 | 27 | The first command will build the source of your application. The second command will package and deploy your application to AWS, with a series of prompts: 28 | 29 | * **Stack Name**: The name of the stack to deploy to CloudFormation. This should be unique to your account and region, and a good starting point would be something matching your project name. 30 | * **AWS Region**: The AWS region you want to deploy your app to. 31 | * **Confirm changes before deploy**: If set to yes, any change sets will be shown to you before execution for manual review. If set to no, the AWS SAM CLI will automatically deploy application changes. 32 | * **Allow SAM CLI IAM role creation**: Many AWS SAM templates, including this example, create AWS IAM roles required for the AWS Lambda function(s) included to access AWS services. By default, these are scoped down to minimum required permissions. To deploy an AWS CloudFormation stack which creates or modifies IAM roles, the `CAPABILITY_IAM` value for `capabilities` must be provided. If permission isn't provided through this prompt, to deploy this example you must explicitly pass `--capabilities CAPABILITY_IAM` to the `sam deploy` command. 33 | * **Save arguments to samconfig.toml**: If set to yes, your choices will be saved to a configuration file inside the project, so that in the future you can just re-run `sam deploy` without parameters to deploy changes to your application. 34 | 35 | You can find your CloudFront Endpoint URL in the output values displayed after deployment. 36 | 37 | ## Deploy static resources to S3 (this should be scripted and made part of the CI/CD process): 38 | 39 | ```bash 40 | aws s3 cp .next/static/ s3:///_next/static --recursive 41 | aws s3 cp public/static s3:///static --recursive 42 | ``` 43 | 44 | ## Use the SAM CLI to build and test locally 45 | 46 | Build your application with the `sam build` command. 47 | 48 | ```bash 49 | nextjs-lambda-sam$ sam build 50 | ``` 51 | 52 | The SAM CLI installs dependencies defined in `nextjs-lambda-sam/package.json`, creates a deployment package, and saves it in the `.aws-sam/build` folder. 53 | 54 | Test a single function by invoking it directly with a test event. An event is a JSON document that represents the input that the function receives from the event source. Test events are included in the `events` folder in this project. 55 | 56 | Run functions locally and invoke them with the `sam local invoke` command. 57 | 58 | ```bash 59 | nextjs-lambda-sam$ sam local invoke NextjsFunction --event events/event.json 60 | ``` 61 | 62 | The SAM CLI can also emulate your application's API. Use the `sam local start-api` to run the API locally on port 3000. 63 | 64 | ```bash 65 | nextjs-lambda-sam$ sam local start-api 66 | nextjs-lambda-sam$ curl http://localhost:3000/ 67 | ``` 68 | 69 | The SAM CLI reads the application template to determine the API's routes and the functions that they invoke. The `Events` property on each function's definition includes the route and method for each path. 70 | 71 | ```yaml 72 | Events: 73 | RootPath: 74 | Type: Api 75 | Properties: 76 | Path: / 77 | Method: ANY 78 | AnyPath: 79 | Type: Api 80 | Properties: 81 | Path: /{proxy+} 82 | Method: ANY 83 | ``` 84 | 85 | ## Add a resource to your application 86 | The application template uses AWS Serverless Application Model (AWS SAM) to define application resources. AWS SAM is an extension of AWS CloudFormation with a simpler syntax for configuring common serverless application resources such as functions, triggers, and APIs. For resources not included in [the SAM specification](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md), you can use standard [AWS CloudFormation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) resource types. 87 | 88 | ## Fetch, tail, and filter Lambda function logs 89 | 90 | To simplify troubleshooting, SAM CLI has a command called `sam logs`. `sam logs` lets you fetch logs generated by your deployed Lambda function from the command line. In addition to printing the logs on the terminal, this command has several nifty features to help you quickly find the bug. 91 | 92 | `NOTE`: This command works for all AWS Lambda functions; not just the ones you deploy using SAM. 93 | 94 | ```bash 95 | nextjs-lambda-sam$ sam logs -n NextjsFunction --stack-name nextjs-lambda-sam --tail 96 | ``` 97 | 98 | You can find more information and examples about filtering Lambda function logs in the [SAM CLI Documentation](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-logging.html). 99 | 100 | ## Cleanup 101 | 102 | To delete the sample application that you created, use the AWS CLI. Assuming you used your project name for the stack name, you can run the following: 103 | 104 | ```bash 105 | aws cloudformation delete-stack --stack-name nextjs-lambda-sam 106 | ``` 107 | 108 | ## Resources 109 | 110 | See the [AWS SAM developer guide](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html) for an introduction to SAM specification, the SAM CLI, and serverless application concepts. 111 | 112 | Next, you can use AWS Serverless Application Repository to deploy ready to use Apps that go beyond hello world samples and learn how authors developed their applications: [AWS Serverless Application Repository main page](https://aws.amazon.com/serverless/serverlessrepo/) 113 | -------------------------------------------------------------------------------- /nextjs-lambda-sam/template.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: > 4 | nextjs-lambda 5 | 6 | SAM Template for Next.js on AWS 7 | 8 | Parameters: 9 | NextBucketName: 10 | Type: String 11 | Description: Bucket name for Next.js static resources 12 | 13 | # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst 14 | Globals: 15 | Function: 16 | Timeout: 3 17 | Tracing: Active 18 | Api: 19 | TracingEnabled: True 20 | 21 | Resources: 22 | NextFunction: 23 | #checkov:skip=CKV_AWS_115: "For demo purposes, not setting function-level concurrent execution limit" 24 | #checkov:skip=CKV_AWS_116: "For demo purposes and costs, not configuring a Dead Letter Queue (DLQ)" 25 | Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction 26 | Properties: 27 | CodeUri: ./ 28 | Handler: run.sh 29 | Runtime: nodejs16.x 30 | MemorySize: 512 31 | Architectures: 32 | - x86_64 33 | Environment: 34 | Variables: 35 | AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap 36 | RUST_LOG: info 37 | PORT: 8080 38 | Layers: 39 | - !Sub 'arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:3' 40 | Events: 41 | RootPath: 42 | Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api 43 | Properties: 44 | Path: / 45 | Method: ANY 46 | AnyPath: 47 | Type: Api 48 | Properties: 49 | Path: /{proxy+} 50 | Method: ANY 51 | Metadata: 52 | BuildMethod: makefile 53 | 54 | NextBucket: 55 | Type: 'AWS::S3::Bucket' 56 | Properties: 57 | BucketName: !Ref NextBucketName 58 | PublicAccessBlockConfiguration: 59 | BlockPublicAcls: true 60 | BlockPublicPolicy: true 61 | IgnorePublicAcls: true 62 | RestrictPublicBuckets: true 63 | LoggingConfiguration: 64 | DestinationBucketName: !Ref NextLoggingBucket 65 | LogFilePrefix: s3-access-logs 66 | VersioningConfiguration: 67 | Status: Enabled 68 | BucketEncryption: 69 | ServerSideEncryptionConfiguration: 70 | - ServerSideEncryptionByDefault: 71 | SSEAlgorithm: 'AES256' 72 | 73 | NextBucketPolicy: 74 | Type: AWS::S3::BucketPolicy 75 | Properties: 76 | Bucket: !Ref NextBucket 77 | PolicyDocument: 78 | Id: NextBucketPolicy 79 | Version: 2012-10-17 80 | Statement: 81 | - Action: 82 | - 's3:GetObject' 83 | Effect: Allow 84 | Principal: 85 | CanonicalUser: !GetAtt NextOriginAccessIdentity.S3CanonicalUserId 86 | Resource: !Join 87 | - '' 88 | - - 'arn:aws:s3:::' 89 | - !Ref NextBucket 90 | - /* 91 | 92 | # The Amazon S3 bucket into which access logs from S3 (for the application) and CloudFront will be put 93 | NextLoggingBucket: 94 | #checkov:skip=CKV_AWS_18: "This bucket is private and only for storing logs" 95 | Type: 'AWS::S3::Bucket' 96 | Properties: 97 | BucketName: !Sub '${NextBucketName}-logs' 98 | PublicAccessBlockConfiguration: 99 | BlockPublicAcls : true 100 | BlockPublicPolicy : true 101 | IgnorePublicAcls : true 102 | RestrictPublicBuckets : true 103 | AccessControl: LogDeliveryWrite 104 | VersioningConfiguration: 105 | Status: Enabled 106 | BucketEncryption: 107 | ServerSideEncryptionConfiguration: 108 | - ServerSideEncryptionByDefault: 109 | SSEAlgorithm: 'AES256' 110 | DeletionPolicy: Delete 111 | 112 | NextOriginAccessIdentity: 113 | Type: AWS::CloudFront::CloudFrontOriginAccessIdentity 114 | Properties: 115 | CloudFrontOriginAccessIdentityConfig: 116 | Comment: OAI for Next static resources in S3 bucket 117 | 118 | NextDistribution: 119 | #checkov:skip=CKV_AWS_68: "For demo purposes and to reduce cost, no WAF is configured" 120 | #checkov:skip=CKV_AWS_174: "CloudFront default certificate sets security policy to TLSv1 regardless" 121 | Type: AWS::CloudFront::Distribution 122 | Properties: 123 | DistributionConfig: 124 | Origins: 125 | - Id: nextS3Origin 126 | DomainName: !GetAtt NextBucket.RegionalDomainName 127 | S3OriginConfig: 128 | OriginAccessIdentity: !Sub 'origin-access-identity/cloudfront/${NextOriginAccessIdentity}' 129 | - Id: nextAPIGatewayOrigin 130 | DomainName: !Sub '${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com' 131 | OriginPath: '/Prod' 132 | CustomOriginConfig: 133 | HTTPSPort: '443' 134 | OriginProtocolPolicy: https-only 135 | Enabled: 'true' 136 | Comment: 'Next.js Distribution' 137 | HttpVersion: http2 138 | DefaultRootObject: '' 139 | DefaultCacheBehavior: 140 | TargetOriginId: nextAPIGatewayOrigin 141 | CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # CachingDisabled 142 | ForwardedValues: 143 | QueryString: 'true' 144 | Cookies: 145 | Forward: all 146 | Compress: 'true' 147 | AllowedMethods: 148 | - DELETE 149 | - GET 150 | - HEAD 151 | - OPTIONS 152 | - PATCH 153 | - POST 154 | - PUT 155 | ViewerProtocolPolicy: redirect-to-https 156 | MaxTTL: '31536000' 157 | CacheBehaviors: 158 | - PathPattern: '/_next/static/*' 159 | TargetOriginId: nextS3Origin 160 | CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized 161 | AllowedMethods: 162 | - GET 163 | - HEAD 164 | ForwardedValues: 165 | QueryString: 'false' 166 | Cookies: 167 | Forward: none 168 | Compress: 'true' 169 | ViewerProtocolPolicy: https-only 170 | - PathPattern: '/static/*' 171 | TargetOriginId: nextS3Origin 172 | CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6 # CachingOptimized 173 | AllowedMethods: 174 | - GET 175 | - HEAD 176 | ForwardedValues: 177 | QueryString: 'false' 178 | Cookies: 179 | Forward: none 180 | Compress: 'true' 181 | ViewerProtocolPolicy: https-only 182 | PriceClass: PriceClass_100 183 | ViewerCertificate: 184 | CloudFrontDefaultCertificate: 'true' 185 | Logging: 186 | Bucket: !GetAtt NextLoggingBucket.RegionalDomainName 187 | Prefix: 'cloudfront-access-logs' 188 | 189 | Outputs: 190 | # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function 191 | # Find out more about other implicit resources you can reference within SAM 192 | # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api 193 | NextApi: 194 | Description: "API Gateway endpoint URL for Prod stage for Next function" 195 | Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/' 196 | NextFunction: 197 | Description: "Next Lambda Function ARN" 198 | Value: !GetAtt NextFunction.Arn 199 | NextFunctionIamRole: 200 | Description: "Implicit IAM Role created for Next function" 201 | Value: !GetAtt NextFunctionRole.Arn 202 | NextBucket: 203 | Description: "S3 bucket for Next static resources" 204 | Value: !GetAtt NextBucket.Arn 205 | NextDistribution: 206 | Description: "CloudFront distribution for Next.js" 207 | Value: !GetAtt NextDistribution.DomainName 208 | --------------------------------------------------------------------------------