|
| 1 | +--- |
| 2 | +title: AWS Lambda |
| 3 | +category: Deploy |
| 4 | +order: 4 |
| 5 | +--- |
| 6 | + |
| 7 | +import Link from "../../../../components/Link.jsx"; |
| 8 | + |
| 9 | +# AWS Lambda |
| 10 | + |
| 11 | +To deploy to AWS, use the built-in `aws` adapter. This adapter deploys your application as an AWS Lambda function with a CloudFront CDN distribution, using AWS SAM (Serverless Application Model) for infrastructure management. |
| 12 | + |
| 13 | +<Link name="prerequisites"> |
| 14 | +## Prerequisites |
| 15 | +</Link> |
| 16 | + |
| 17 | +You need the following tools installed: |
| 18 | + |
| 19 | +- **AWS CLI** — for managing AWS credentials |
| 20 | +- **AWS SAM CLI** — for building and deploying the SAM template |
| 21 | + |
| 22 | +```sh |
| 23 | +brew install awscli aws-sam-cli |
| 24 | +``` |
| 25 | + |
| 26 | +Then configure your AWS credentials: |
| 27 | + |
| 28 | +```sh |
| 29 | +aws configure |
| 30 | +``` |
| 31 | + |
| 32 | +You'll need your AWS Access Key ID and Secret Access Key. You can create these in the [AWS IAM Console](https://console.aws.amazon.com/iam/) under **Users → Security credentials → Access keys**. |
| 33 | + |
| 34 | +<Link name="installation"> |
| 35 | +## Installation |
| 36 | +</Link> |
| 37 | + |
| 38 | +No additional packages are needed — the adapter is built into `@lazarv/react-server`. |
| 39 | + |
| 40 | +Add the adapter to your `react-server.config.mjs` file: |
| 41 | + |
| 42 | +```mjs |
| 43 | +export default { |
| 44 | + adapter: "aws", |
| 45 | +}; |
| 46 | +``` |
| 47 | + |
| 48 | +<Link name="configuration"> |
| 49 | +## Configuration |
| 50 | +</Link> |
| 51 | + |
| 52 | +You can customize the adapter by passing options: |
| 53 | + |
| 54 | +```mjs |
| 55 | +export default { |
| 56 | + adapter: [ |
| 57 | + "aws", |
| 58 | + { |
| 59 | + name: "my-app", // Application name (used in resource names) |
| 60 | + stackName: "my-app-stack", // CloudFormation stack name |
| 61 | + runtime: "nodejs20.x", // Lambda runtime (default: "nodejs20.x") |
| 62 | + memorySize: 1024, // Lambda memory in MB (default: 1024) |
| 63 | + timeout: 30, // Lambda timeout in seconds (default: 30) |
| 64 | + architecture: "arm64", // Lambda architecture (default: "arm64") |
| 65 | + authType: "NONE", // Function URL auth type (default: "NONE") |
| 66 | + environment: { // Additional environment variables |
| 67 | + MY_API_KEY: "value", |
| 68 | + }, |
| 69 | + }, |
| 70 | + ], |
| 71 | +}; |
| 72 | +``` |
| 73 | + |
| 74 | +### Configuration Options |
| 75 | + |
| 76 | +- `name`: Application name used for AWS resource names. Falls back to `package.json` name (without scope) or `"react-server-app"`. |
| 77 | +- `stackName`: CloudFormation stack name used during deployment. Defaults to `name`. |
| 78 | +- `runtime`: Lambda runtime identifier (default: `"nodejs20.x"`). |
| 79 | +- `memorySize`: Lambda function memory in MB (default: `1024`). |
| 80 | +- `timeout`: Lambda function timeout in seconds (default: `30`). |
| 81 | +- `architecture`: Lambda CPU architecture, `"arm64"` or `"x86_64"` (default: `"arm64"`). Arm64 (Graviton2) offers better price-performance. |
| 82 | +- `authType`: Function URL authentication type (default: `"NONE"`). Set to `"AWS_IAM"` to require IAM auth. |
| 83 | +- `environment`: Additional environment variables added to the Lambda function. `NODE_ENV` is always set to `"production"`. |
| 84 | +- `functionProperties`: Additional SAM `AWS::Serverless::Function` properties merged into the function resource. |
| 85 | +- `cloudfront`: Set to `false` to skip CloudFront distribution creation. Use `cloudfront.distributionConfig` to extend the generated CloudFront `DistributionConfig`. |
| 86 | +- `resources`: Additional CloudFormation resources merged into the template. |
| 87 | +- `outputs`: Additional CloudFormation outputs merged into the template. |
| 88 | +- `template`: Override or extend the SAM template. Pass an object to merge, or a function `(template) => template` for full control. |
| 89 | +- `deployArgs`: Additional arguments passed to `sam deploy`. |
| 90 | + |
| 91 | +<Link name="deploy"> |
| 92 | +## Deploy |
| 93 | +</Link> |
| 94 | + |
| 95 | +Build and deploy your application: |
| 96 | + |
| 97 | +```sh |
| 98 | +pnpm react-server build [root] --adapter aws |
| 99 | +sam deploy --guided --stack-name my-app --capabilities CAPABILITY_IAM |
| 100 | +``` |
| 101 | + |
| 102 | +Or use the `--deploy` flag to build and deploy in one step: |
| 103 | + |
| 104 | +```sh |
| 105 | +pnpm react-server build [root] --adapter aws --deploy |
| 106 | +``` |
| 107 | + |
| 108 | +The first deployment uses `--guided` mode which prompts for configuration and saves it to `samconfig.toml`. Subsequent deployments use the saved configuration automatically. |
| 109 | + |
| 110 | +<Link name="architecture"> |
| 111 | +## Architecture |
| 112 | +</Link> |
| 113 | + |
| 114 | +The adapter creates the following AWS resources: |
| 115 | + |
| 116 | +- **Lambda Function** with a Function URL (streaming enabled via `RESPONSE_STREAM`) |
| 117 | +- **CloudFront Distribution** as a CDN in front of the Lambda function |
| 118 | +- **CloudFront Cache Policy** that respects origin `Cache-Control` headers |
| 119 | + |
| 120 | +### How it works |
| 121 | + |
| 122 | +All requests flow through a single Lambda origin: |
| 123 | + |
| 124 | +``` |
| 125 | +Viewer → CloudFront → Lambda |
| 126 | + ├─ Static file? → Serve from disk with Cache-Control |
| 127 | + └─ Dynamic? → SSR via react-server |
| 128 | +``` |
| 129 | + |
| 130 | +The Lambda handler includes all static files in its deployment package and serves them directly from the filesystem. Each response includes appropriate `Cache-Control` headers: |
| 131 | + |
| 132 | +- **Build assets** (`/assets/*`, `/client/*`): `public, max-age=31536000, immutable` — cached at the edge for 1 year (filenames are content-hashed) |
| 133 | +- **Pre-rendered HTML / x-component**: `must-revalidate` — CloudFront always revalidates with Lambda |
| 134 | +- **Public files**: `public, max-age=600` — cached for 10 minutes |
| 135 | + |
| 136 | +After the first request, CloudFront serves cached static files directly from the edge without invoking Lambda. The custom cache policy (`DefaultTTL: 0`, `MinTTL: 0`, `MaxTTL: 31536000`) defers entirely to the origin's `Cache-Control` headers. |
| 137 | + |
| 138 | +<Link name="cloudfront-configuration"> |
| 139 | +## CloudFront Configuration |
| 140 | +</Link> |
| 141 | + |
| 142 | +### Custom domain |
| 143 | + |
| 144 | +To use a custom domain with your CloudFront distribution, extend the distribution config: |
| 145 | + |
| 146 | +```mjs |
| 147 | +export default { |
| 148 | + adapter: [ |
| 149 | + "aws", |
| 150 | + { |
| 151 | + cloudfront: { |
| 152 | + distributionConfig: { |
| 153 | + Aliases: ["www.example.com"], |
| 154 | + ViewerCertificate: { |
| 155 | + AcmCertificateArn: "arn:aws:acm:us-east-1:123456789:certificate/abc-123", |
| 156 | + SslSupportMethod: "sni-only", |
| 157 | + MinimumProtocolVersion: "TLSv1.2_2021", |
| 158 | + }, |
| 159 | + }, |
| 160 | + }, |
| 161 | + }, |
| 162 | + ], |
| 163 | +}; |
| 164 | +``` |
| 165 | + |
| 166 | +> **Note:** ACM certificates for CloudFront must be in the `us-east-1` region. |
| 167 | +
|
| 168 | +### Without CloudFront |
| 169 | + |
| 170 | +To deploy without CloudFront (Lambda Function URL only): |
| 171 | + |
| 172 | +```mjs |
| 173 | +export default { |
| 174 | + adapter: [ |
| 175 | + "aws", |
| 176 | + { |
| 177 | + cloudfront: false, |
| 178 | + }, |
| 179 | + ], |
| 180 | +}; |
| 181 | +``` |
| 182 | + |
| 183 | +<Link name="sam-template"> |
| 184 | +## SAM Template |
| 185 | +</Link> |
| 186 | + |
| 187 | +The adapter generates a `template.json` file in your project root. This is a standard AWS SAM template that can be customized via `adapterOptions.template`: |
| 188 | + |
| 189 | +```mjs |
| 190 | +export default { |
| 191 | + adapter: [ |
| 192 | + "aws", |
| 193 | + { |
| 194 | + template: (template) => { |
| 195 | + // Add a DynamoDB table |
| 196 | + template.Resources.MyTable = { |
| 197 | + Type: "AWS::DynamoDB::Table", |
| 198 | + Properties: { |
| 199 | + TableName: "my-table", |
| 200 | + AttributeDefinitions: [ |
| 201 | + { AttributeName: "id", AttributeType: "S" }, |
| 202 | + ], |
| 203 | + KeySchema: [ |
| 204 | + { AttributeName: "id", KeyType: "HASH" }, |
| 205 | + ], |
| 206 | + BillingMode: "PAY_PER_REQUEST", |
| 207 | + }, |
| 208 | + }; |
| 209 | + return template; |
| 210 | + }, |
| 211 | + }, |
| 212 | + ], |
| 213 | +}; |
| 214 | +``` |
| 215 | + |
| 216 | +<Link name="troubleshooting"> |
| 217 | +## Troubleshooting |
| 218 | +</Link> |
| 219 | + |
| 220 | +### `zsh: command not found: sam` |
| 221 | + |
| 222 | +Install the AWS SAM CLI: |
| 223 | + |
| 224 | +```sh |
| 225 | +brew install aws-sam-cli |
| 226 | +``` |
| 227 | + |
| 228 | +### `zsh: command not found: aws` |
| 229 | + |
| 230 | +Install the AWS CLI: |
| 231 | + |
| 232 | +```sh |
| 233 | +brew install awscli |
| 234 | +``` |
| 235 | + |
| 236 | +### `Error: Unable to locate credentials` |
| 237 | + |
| 238 | +Configure your AWS credentials: |
| 239 | + |
| 240 | +```sh |
| 241 | +aws configure |
| 242 | +``` |
| 243 | + |
| 244 | +You'll need your Access Key ID, Secret Access Key, default region, and output format. Visit the [AWS IAM Console](https://console.aws.amazon.com/iam/) to create access keys. |
| 245 | + |
| 246 | +If you're using AWS SSO: |
| 247 | + |
| 248 | +```sh |
| 249 | +aws sso login --profile your-profile |
| 250 | +``` |
| 251 | + |
| 252 | +### `Failed to create/update the stack ... ROLLBACK_COMPLETE` |
| 253 | + |
| 254 | +A previous deployment failed and CloudFormation is stuck. Delete the failed stack and try again: |
| 255 | + |
| 256 | +```sh |
| 257 | +sam delete --stack-name your-stack-name |
| 258 | +sam deploy --guided --stack-name your-stack-name --capabilities CAPABILITY_IAM |
| 259 | +``` |
| 260 | + |
| 261 | +### Lambda cold starts |
| 262 | + |
| 263 | +Cold starts are expected with Lambda functions. To minimize their impact: |
| 264 | + |
| 265 | +- Use `arm64` architecture (default) — Graviton2 processors have faster cold starts |
| 266 | +- Increase `memorySize` — Lambda allocates CPU proportional to memory |
| 267 | +- Consider using [Lambda Provisioned Concurrency](https://docs.aws.amazon.com/lambda/latest/dg/provisioned-concurrency.html) for production workloads |
| 268 | + |
| 269 | +You can add provisioned concurrency via the template override: |
| 270 | + |
| 271 | +```mjs |
| 272 | +export default { |
| 273 | + adapter: [ |
| 274 | + "aws", |
| 275 | + { |
| 276 | + template: (template) => { |
| 277 | + const fnKey = Object.keys(template.Resources).find( |
| 278 | + (k) => template.Resources[k].Type === "AWS::Serverless::Function" |
| 279 | + ); |
| 280 | + if (fnKey) { |
| 281 | + template.Resources[fnKey].Properties.ProvisionedConcurrencyConfig = { |
| 282 | + ProvisionedConcurrentExecutions: 1, |
| 283 | + }; |
| 284 | + } |
| 285 | + return template; |
| 286 | + }, |
| 287 | + }, |
| 288 | + ], |
| 289 | +}; |
| 290 | +``` |
| 291 | + |
| 292 | +### CloudFront cache invalidation |
| 293 | + |
| 294 | +After redeploying, CloudFront may still serve stale cached content. Create an invalidation: |
| 295 | + |
| 296 | +```sh |
| 297 | +aws cloudfront create-invalidation \ |
| 298 | + --distribution-id YOUR_DISTRIBUTION_ID \ |
| 299 | + --paths "/*" |
| 300 | +``` |
| 301 | + |
| 302 | +Build assets with content-hashed filenames (`/assets/*`, `/client/*`) don't need invalidation — new deployments generate new filenames automatically. |
| 303 | + |
| 304 | +> **Note:** For additional AWS-specific features like VPC configuration, IAM policies, or Lambda layers, use the `functionProperties`, `resources`, and `template` options. Refer to the [AWS SAM documentation](https://docs.aws.amazon.com/serverless-application-model/) for the full template specification. |
0 commit comments