├── .gitignore ├── .upignore ├── README.md ├── package.json ├── pages ├── about.js └── index.js ├── server.js ├── up.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next -------------------------------------------------------------------------------- /.upignore: -------------------------------------------------------------------------------- 1 | !.next -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js with AWS Lambda 2 | 3 | This is a tiny example to show how to deploy the Next.js app on AWS Lambda using [Apex Up](https://up.docs.apex.sh/#introduction). 4 | 5 | - Post : https://moondaddi.dev/post/2019-01-11-Deploy-Nextjs-app-to-AWS-Lambda/ 6 | 7 | > You may encounter the cold start issue, then refer to the lambda warmer. 8 | > https://github.com/mattdamon108/lambda-warmer 9 | 10 | - Post : https://moondaddi.dev/post/2019-03-23-how-to-warm-lambda-function/ 11 | 12 | ## Next.js app with custom server 13 | 14 | The custom server for Next.js app is needed to run your app on AWS Lambda. In this example, `express` will be used. 15 | 16 | ```javascript 17 | // server.js 18 | 19 | const express = require("express"); 20 | const next = require("next"); 21 | 22 | const port = parseInt(process.env.PORT, 10) || 3000; 23 | const dev = process.env.NODE_ENV !== "production"; 24 | const app = next({ dev }); 25 | const handle = app.getRequestHandler(); 26 | 27 | app 28 | .prepare() 29 | .then(() => { 30 | const server = express(); 31 | 32 | server.get("/", (req, res) => { 33 | return app.render(req, res, "/", req.params); 34 | }); 35 | 36 | server.get("/about", (req, res) => { 37 | return app.render(req, res, "/about", req.params); 38 | }); 39 | 40 | server.get("*", (req, res) => { 41 | return handle(req, res); 42 | }); 43 | 44 | server.listen(port, err => { 45 | if (err) throw err; 46 | console.log(`> Ready on http://localhost:${port}`); 47 | }); 48 | }) 49 | .catch(ex => { 50 | console.log(ex); 51 | process.exit(1); 52 | }); 53 | ``` 54 | 55 | ## update `package.json` with custom `server.js` 56 | 57 | ```json 58 | { 59 | "scripts": { 60 | "dev": "node server.js", 61 | "build": "next build", 62 | "start": "NODE_ENV=production node server.js" 63 | } 64 | } 65 | ``` 66 | 67 | ## Install Apex Up 68 | 69 | ```shell 70 | $ curl -sf https://up.apex.sh/install | sh 71 | 72 | # verify the installation 73 | $ up version 74 | ``` 75 | 76 | ## AWS credentials 77 | 78 | You need to have one AWS account and recommend to use IAM with programmaic way for security and convinience. If you have already installed `awscli` or `awsebcli`, etc. You are having `~/.aws/credentials` which is storing AWS credentials. It allows you to use `AWS_PROFILE` environment. If you don't please make one and save it with your account `access key` and `security key` in it. 79 | 80 | ```shell 81 | # ~/.aws/credentials 82 | 83 | [my-aws-account-for-lambda] 84 | aws_access_key = xxxxxxxxxxxxx 85 | aws_secret_access_key = xxxxxxxxxxxxxxxxxxxx 86 | ``` 87 | 88 | ## IAM policy for Up CLI 89 | 90 | IAM policy allows the Up to access your AWS resources in order to deploy your Next.js app on Lambda. Go to [AWS IAM](https://aws.amazon.com/iam/) and make the new policy and link it up to your AWS account. 91 | 92 | ```json 93 | { 94 | "Version": "2012-10-17", 95 | "Statement": [ 96 | { 97 | "Effect": "Allow", 98 | "Action": [ 99 | "acm:*", 100 | "cloudformation:Create*", 101 | "cloudformation:Delete*", 102 | "cloudformation:Describe*", 103 | "cloudformation:ExecuteChangeSet", 104 | "cloudformation:Update*", 105 | "cloudfront:*", 106 | "cloudwatch:*", 107 | "ec2:*", 108 | "ecs:*", 109 | "events:*", 110 | "iam:AttachRolePolicy", 111 | "iam:CreatePolicy", 112 | "iam:CreateRole", 113 | "iam:DeleteRole", 114 | "iam:DeleteRolePolicy", 115 | "iam:GetRole", 116 | "iam:PassRole", 117 | "iam:PutRolePolicy", 118 | "lambda:AddPermission", 119 | "lambda:Create*", 120 | "lambda:Delete*", 121 | "lambda:Get*", 122 | "lambda:InvokeFunction", 123 | "lambda:List*", 124 | "lambda:RemovePermission", 125 | "lambda:Update*", 126 | "logs:Create*", 127 | "logs:Describe*", 128 | "logs:FilterLogEvents", 129 | "logs:Put*", 130 | "logs:Test*", 131 | "route53:*", 132 | "route53domains:*", 133 | "s3:*", 134 | "ssm:*", 135 | "sns:*" 136 | ], 137 | "Resource": "*" 138 | }, 139 | { 140 | "Effect": "Allow", 141 | "Action": "apigateway:*", 142 | "Resource": "arn:aws:apigateway:*::/*" 143 | } 144 | ] 145 | } 146 | ``` 147 | 148 | ## Create `up.json` file 149 | 150 | ```js 151 | { 152 | "name": "nextjs-example", 153 | // aws account profile in ~/.aws/credentials 154 | "profile": "my-aws-account-for-lambda", 155 | "regions": ["ap-northeast-2"], 156 | "lambda": { 157 | // min 128, default 512 158 | "memory": 256, 159 | // AWS Lambda supports node.js 8.10 latest 160 | "runtime": "nodejs8.10" 161 | }, 162 | "proxy": { 163 | "command": "npm start", 164 | "timeout": 25, 165 | "listen_timeout": 15, 166 | "shutdown_timeout": 15 167 | }, 168 | "stages": { 169 | "development": { 170 | "proxy": { 171 | "command": "yarn dev" 172 | } 173 | } 174 | }, 175 | "environment": { 176 | // you can hydrate env variables as you want. 177 | "NODE_ENV": "production" 178 | }, 179 | "error_pages": { 180 | "variables": { 181 | "support_email": "admin@my-email.com", 182 | "color": "#2986e2" 183 | } 184 | } 185 | } 186 | ``` 187 | 188 | ## Build the Next.js app before deploy 189 | 190 | ```shell 191 | $ yarn build 192 | ``` 193 | 194 | ## Create `.upignore` file 195 | 196 | The Up will inspect your files to compose and deploy to lambda. Firstly The up will read `.gitignore` and ignore files written in `.gitignore`. And after that, `.upignore` will be read. The Up, by default, ignores dotfiles, so needs to negate `.next` directory in `.upignore` in order for the Up will build the package with it. 197 | 198 | ``` 199 | # .upignore 200 | 201 | !.next 202 | ``` 203 | 204 | ## Deploy 205 | 206 | ```shell 207 | $ up 208 | ``` 209 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-with-lambda", 3 | "version": "1.0.0", 4 | "description": "Next.js example deploy to AWS Lambda", 5 | "main": "index.js", 6 | "repository": "https://github.com/mattdamon108/nextjs-with-lambda.git", 7 | "author": "mattdamon108 ", 8 | "license": "MIT", 9 | "dependencies": { 10 | "express": "^4.16.4", 11 | "next": "^7.0.2", 12 | "react": "^16.7.0", 13 | "react-dom": "^16.7.0" 14 | }, 15 | "scripts": { 16 | "dev": "next", 17 | "build": "next build", 18 | "start": "NODE_ENV=production node server.js" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /pages/about.js: -------------------------------------------------------------------------------- 1 | export default () =>
About page
; 2 | -------------------------------------------------------------------------------- /pages/index.js: -------------------------------------------------------------------------------- 1 | export default () =>
Hi~ I'm running on Lambda.
; 2 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const next = require("next"); 3 | 4 | const port = parseInt(process.env.PORT, 10) || 3000; 5 | const dev = process.env.NODE_ENV !== "production"; 6 | const app = next({ dev }); 7 | const handle = app.getRequestHandler(); 8 | 9 | app 10 | .prepare() 11 | .then(() => { 12 | const server = express(); 13 | 14 | server.get("/", (req, res) => { 15 | return app.render(req, res, "/", req.params); 16 | }); 17 | 18 | server.get("/about", (req, res) => { 19 | return app.render(req, res, "/about", req.params); 20 | }); 21 | 22 | server.get("*", (req, res) => { 23 | return handle(req, res); 24 | }); 25 | 26 | server.listen(port, err => { 27 | if (err) throw err; 28 | console.log(`> Ready on http://localhost:${port}`); 29 | }); 30 | }) 31 | .catch(ex => { 32 | console.log(ex); 33 | process.exit(1); 34 | }); 35 | -------------------------------------------------------------------------------- /up.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nextjs-example", 3 | "profile": "my-aws-account-for-lambda", 4 | "regions": ["ap-northeast-2"], 5 | "lambda": { 6 | "memory": 256, 7 | "runtime": "nodejs8.10" 8 | }, 9 | "proxy": { 10 | "command": "npm start", 11 | "timeout": 25, 12 | "listen_timeout": 15, 13 | "shutdown_timeout": 15 14 | }, 15 | "stages": { 16 | "development": { 17 | "proxy": { 18 | "command": "yarn dev" 19 | } 20 | } 21 | }, 22 | "environment": {}, 23 | "error_pages": { 24 | "variables": { 25 | "support_email": "myaccount@my.com", 26 | "color": "#2986e2" 27 | } 28 | } 29 | } 30 | --------------------------------------------------------------------------------