---
# === IDENTITY ===
id: software/devops/aws-lambda-sam/2026
canonical_question: "AWS Lambda + API Gateway reference (SAM template)"
aliases:
  - "How to deploy Lambda with SAM template"
  - "SAM template.yaml for API Gateway and Lambda"
  - "AWS serverless application model reference"
  - "sam build sam deploy Lambda guide"
entity_type: software_reference
domain: software > devops > AWS Lambda SAM
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-28
confidence: 0.93
version: 1.0
first_published: 2026-02-28

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "SAM CLI 1.100+ unified build behavior (2024)"
  next_review: 2026-08-27
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Lambda max execution timeout is 900 seconds (15 minutes) — use Step Functions for longer workflows"
  - "Deployment package size limit: 50 MB zipped, 250 MB unzipped (use Lambda Layers for large dependencies)"
  - "API Gateway payload limit is 10 MB for REST API and 6 MB for HTTP API"
  - "SAM CLI requires Docker for --use-container builds and sam local invoke"
  - "CloudFormation stack name must be unique per region per account"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "You need container-based Lambda (Docker images instead of zip)"
    use_instead: "AWS Lambda container image deployment guide"
  - condition: "You are using CDK instead of SAM"
    use_instead: "AWS CDK Lambda + API Gateway reference"

# === AGENT HINTS ===
inputs_needed:
  - key: "runtime"
    question: "Which Lambda runtime are you targeting?"
    type: choice
    options: ["python3.12", "nodejs20.x", "nodejs22.x", "java21", "dotnet8", "go (provided.al2023)"]
  - key: "api_type"
    question: "REST API (v1) or HTTP API (v2)?"
    type: choice
    options: ["REST API (AWS::Serverless::Api)", "HTTP API (AWS::Serverless::HttpApi)"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/aws-lambda-sam/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-28)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to:
    - id: software/devops/terraform-aws-basic/2026
      label: "Terraform AWS Basic Infrastructure"
  solves: []
  alternative_to:
    - id: software/devops/cloudflare-workers-setup/2026
      label: "Cloudflare Workers Setup Reference"
  often_confused_with: []

# === SOURCES ===
sources:
  - id: src1
    title: "AWS SAM template specification"
    author: AWS
    url: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src2
    title: "AWS::Serverless::Function resource"
    author: AWS
    url: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-function.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src3
    title: "AWS::Serverless::Api resource"
    author: AWS
    url: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-api.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src4
    title: "sam build command reference"
    author: AWS
    url: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-build.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src5
    title: "sam deploy command reference"
    author: AWS
    url: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-cli-command-reference-sam-deploy.html
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src6
    title: "Mistakes to avoid with serverless Lambda"
    author: AWS Architecture Blog
    url: https://aws.amazon.com/blogs/architecture/mistakes-to-avoid-when-implementing-serverless-architecture-with-lambda/
    type: technical_blog
    published: 2024-06-10
    reliability: high
  - id: src7
    title: "Operating Lambda: Anti-patterns in event-driven architectures"
    author: AWS Compute Blog
    url: https://aws.amazon.com/blogs/compute/operating-lambda-anti-patterns-in-event-driven-architectures-part-3/
    type: technical_blog
    published: 2024-03-20
    reliability: high
---

# AWS Lambda + API Gateway Reference (SAM Template)

## TL;DR

- **Bottom line**: AWS SAM (Serverless Application Model) is the fastest way to define, build, test locally, and deploy Lambda functions with API Gateway — a single `template.yaml` replaces hundreds of lines of raw CloudFormation.
- **Key tool/command**: `sam init && sam build && sam deploy --guided`
- **Watch out for**: Forgetting `--use-container` when your Lambda has native dependencies (e.g., numpy, Pillow) — the build succeeds locally but fails at runtime on Lambda's Amazon Linux 2023.
- **Works with**: SAM CLI 1.100+, AWS CLI v2, Python 3.12 / Node.js 20.x–22.x / Java 21 / .NET 8 / Go on provided.al2023.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Lambda max timeout is 900 seconds (15 min) — anything longer must use Step Functions or ECS/Fargate.
- Deployment package: 50 MB zipped / 250 MB unzipped (layers count toward unzipped limit).
- API Gateway REST API payload limit: 10 MB. HTTP API (v2): 6 MB.
- SAM requires `Transform: AWS::Serverless-2016-10-31` — without it, SAM resource types are rejected by CloudFormation.
- Environment variables in `template.yaml` are stored in plaintext in CloudFormation — use SSM Parameter Store or Secrets Manager for secrets.

## Quick Reference

| Setting | Value / Default | Notes |
|---|---|---|
| `Runtime` | `python3.12`, `nodejs20.x`, `nodejs22.x`, `java21` | `python3.8`–`3.11` deprecated 2024+ |
| `Handler` | `app.lambda_handler` (Python), `app.handler` (Node) | Relative to `CodeUri` |
| `CodeUri` | `./src/` | Path to function code |
| `MemorySize` | `128` MB (default), max `10240` MB | CPU scales linearly with memory |
| `Timeout` | `3` sec (default), max `900` sec | Set realistically — overshoot costs money |
| `Architectures` | `x86_64` (default) or `arm64` | arm64 (Graviton2) is 20% cheaper |
| `Tracing` | `Active` or `PassThrough` | `Active` enables X-Ray |
| `AutoPublishAlias` | `live` | Enables gradual deploy via `DeploymentPreference` |
| `Environment.Variables` | key-value map | Plaintext in CFN — use SSM refs for secrets |
| `Layers` | list of Layer ARNs | Max 5 layers per function |
| `Events.Api.Type` | `Api` (REST v1) or `HttpApi` (v2) | HTTP API is cheaper and faster |
| `Events.Api.Properties.Path` | `/items/{id}` | Supports path parameters |
| `Events.Api.Properties.Method` | `get`, `post`, `put`, `delete` | Case-insensitive |
| `Globals.Function` | Shared defaults | Override per-function as needed |

## Decision Tree

```
START
├── Need local testing with hot-reload?
│   ├── YES → Use `sam local start-api --warm-containers EAGER`
│   └── NO ↓
├── Function has native C/C++ dependencies?
│   ├── YES → Always use `sam build --use-container`
│   └── NO ↓
├── Multiple functions sharing code?
│   ├── YES → Create a Lambda Layer (AWS::Serverless::LayerVersion)
│   └── NO ↓
├── Need WebSocket or real-time?
│   ├── YES → Use AWS::ApiGatewayV2::Api with $connect/$disconnect
│   └── NO ↓
├── Need <$3.50/million requests and <30s latency OK?
│   ├── YES → Use HttpApi (v2) — cheaper, auto-CORS, JWT auth built-in
│   └── NO ↓
├── Need request validation, API keys, usage plans?
│   ├── YES → Use Api (REST v1) — full API Gateway feature set
│   └── NO ↓
└── DEFAULT → Use HttpApi (v2) for most new projects
```

## Step-by-Step Guide

### 1. Initialize a new SAM project

Create a project from an official starter template. [src1]

```bash
sam init --runtime python3.12 --app-template hello-world --name my-api
cd my-api
```

**Verify**: `ls template.yaml` → file exists with `Transform: AWS::Serverless-2016-10-31`

### 2. Define function and API in template.yaml

Write the SAM template with a Lambda function behind API Gateway. [src2] [src3]

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My API - Lambda + API Gateway

Globals:
  Function:
    Timeout: 30
    MemorySize: 256
    Runtime: python3.12
    Architectures:
      - arm64
    Environment:
      Variables:
        STAGE: !Ref Stage

Parameters:
  Stage:
    Type: String
    Default: dev
    AllowedValues: [dev, staging, prod]

Resources:
  GetItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.get_items
      Description: List all items
      Events:
        GetItems:
          Type: HttpApi
          Properties:
            Path: /items
            Method: get

  GetItemByIdFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.get_item_by_id
      Description: Get a single item by ID
      Events:
        GetItem:
          Type: HttpApi
          Properties:
            Path: /items/{id}
            Method: get

  CreateItemFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.create_item
      Description: Create a new item
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ItemsTable
      Events:
        CreateItem:
          Type: HttpApi
          Properties:
            Path: /items
            Method: post

  ItemsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub "${Stage}-items"
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH

Outputs:
  ApiUrl:
    Description: API Gateway endpoint URL
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/"
```

**Verify**: `sam validate` → `template.yaml is a valid SAM Template`

### 3. Write the Lambda handler

Create the Python handler with proper error handling. [src2]

```python
# src/app.py
import json
import boto3
import os
from decimal import Decimal

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(f"{os.environ.get('STAGE', 'dev')}-items")

def _response(status_code, body):
    return {
        'statusCode': status_code,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps(body, default=str)
    }

def get_items(event, context):
    try:
        result = table.scan(Limit=100)
        return _response(200, result.get('Items', []))
    except Exception as e:
        return _response(500, {'error': str(e)})

def get_item_by_id(event, context):
    item_id = event['pathParameters']['id']
    try:
        result = table.get_item(Key={'id': item_id})
        item = result.get('Item')
        if not item:
            return _response(404, {'error': 'Item not found'})
        return _response(200, item)
    except Exception as e:
        return _response(500, {'error': str(e)})

def create_item(event, context):
    try:
        body = json.loads(event.get('body', '{}'))
        if 'id' not in body:
            return _response(400, {'error': 'id is required'})
        table.put_item(Item=body)
        return _response(201, body)
    except json.JSONDecodeError:
        return _response(400, {'error': 'Invalid JSON'})
    except Exception as e:
        return _response(500, {'error': str(e)})
```

**Verify**: `python -c "from src.app import get_items; print('OK')"` → `OK`

### 4. Build the application

Compile dependencies and prepare deployment artifacts. [src4]

```bash
# Standard build
sam build

# If you have native dependencies (numpy, Pillow, psycopg2):
sam build --use-container

# For parallel builds across multiple functions:
sam build --parallel
```

**Verify**: `ls .aws-sam/build/` → one directory per function listed in template

### 5. Test locally

Run the API locally before deploying. [src1]

```bash
# Start local API Gateway emulator
sam local start-api --warm-containers EAGER

# Invoke a single function
sam local invoke GetItemsFunction --event events/get_items.json
```

**Verify**: `curl http://127.0.0.1:3000/items` → returns JSON array

### 6. Deploy to AWS

Deploy with guided mode on first run, then use saved config. [src5]

```bash
# First deploy — interactive prompts, saves config to samconfig.toml
sam deploy --guided

# Subsequent deploys — uses saved config
sam deploy

# Deploy to a specific stage
sam deploy --parameter-overrides Stage=prod
```

**Verify**: `aws cloudformation describe-stacks --stack-name my-api --query 'Stacks[0].Outputs'` → shows ApiUrl

## Code Examples

### Python: Lambda with API Gateway proxy integration

```python
# Input:  API Gateway event (HttpApi v2 format)
# Output: HTTP response dict with statusCode, headers, body

import json
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def handler(event, context):
    logger.info(f"Request: {event['requestContext']['http']['method']} "
                f"{event['rawPath']}")

    path = event.get('rawPath', '/')
    method = event['requestContext']['http']['method']
    query = event.get('queryStringParameters', {}) or {}

    if path == '/health' and method == 'GET':
        return {
            'statusCode': 200,
            'body': json.dumps({'status': 'healthy',
                                'remaining_ms': context.get_remaining_time_in_millis()})
        }

    return {'statusCode': 404, 'body': json.dumps({'error': 'Not found'})}
```

### Node.js: Lambda with DynamoDB and structured logging

> Full script: [node-js-lambda-with-dynamodb-and-structured-loggin.js](scripts/node-js-lambda-with-dynamodb-and-structured-loggin.js) (25 lines)

```javascript
// Input:  API Gateway event (HttpApi v2 format)
// Output: HTTP response object
const { DynamoDBClient } = require('@aws-sdk/client-dynamodb');
const { DynamoDBDocumentClient, GetCommand } = require('@aws-sdk/lib-dynamodb');
const client = new DynamoDBClient({});
# ... (see full script)
```

### YAML: SAM template with REST API (v1), authorizer, and CORS

> Full script: [yaml-sam-template-with-rest-api-v1-authorizer-and-.yml](scripts/yaml-sam-template-with-rest-api-v1-authorizer-and-.yml) (35 lines)

```yaml
# Input:  SAM template.yaml
# Output: REST API with Cognito authorizer and CORS
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
# ... (see full script)
```

## Anti-Patterns

### Wrong: Monolithic Lambda (Lambdalith)

```python
# BAD — single function handles all routes
def handler(event, context):
    path = event['rawPath']
    if path == '/users':
        return handle_users(event)
    elif path == '/orders':
        return handle_orders(event)
    elif path == '/payments':
        return handle_payments(event)
    elif path == '/reports':
        return handle_reports(event)
    # 50 more routes...
```

### Correct: One function per route (or per resource group)

> Full script: [correct-one-function-per-route-or-per-resource-gro.yml](scripts/correct-one-function-per-route-or-per-resource-gro.yml) (28 lines)

```yaml
# GOOD — separate functions with targeted permissions
Resources:
  UsersFunction:
    Type: AWS::Serverless::Function
    Properties:
# ... (see full script)
```

### Wrong: Hardcoded secrets in template.yaml

```yaml
# BAD — secrets visible in CloudFormation console and version control
Environment:
  Variables:
    DB_PASSWORD: "my-secret-password-123"
    API_KEY: "sk-live-abc123"
```

### Correct: Use SSM Parameter Store or Secrets Manager references

```yaml
# GOOD — resolved at deploy time, not stored in template
Environment:
  Variables:
    DB_PASSWORD: '{{resolve:ssm-secure:/myapp/db-password}}'
    API_KEY: '{{resolve:secretsmanager:myapp/api-key:SecretString:key}}'
```

### Wrong: No Globals section — duplicating config across functions

```yaml
# BAD — 10 functions each repeating the same runtime, timeout, memory
Resources:
  Func1:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      Timeout: 30
      MemorySize: 256
      Architectures: [arm64]
      # ... repeated for every function
```

### Correct: Use Globals to set shared defaults

```yaml
# GOOD — shared config in one place, override per-function only when needed
Globals:
  Function:
    Runtime: python3.12
    Timeout: 30
    MemorySize: 256
    Architectures:
      - arm64
    Tracing: Active

Resources:
  Func1:
    Type: AWS::Serverless::Function
    Properties:
      Handler: func1.handler
      CodeUri: src/
      # Inherits all Globals
```

## Common Pitfalls

- **Cold starts in VPC-attached Lambdas**: Attaching Lambda to a VPC adds 1-10s cold start. Fix: use `VpcConfig` only when necessary, or enable Provisioned Concurrency (`ProvisionedConcurrencyConfig`). [src6]
- **Missing `--use-container` for native deps**: Building locally on macOS/Windows produces incompatible binaries. Fix: always use `sam build --use-container` for packages with C extensions. [src4]
- **Forgetting to set `Timeout` above default 3s**: API calls that take >3s fail silently. Fix: set `Timeout` in `Globals.Function` to a reasonable value (15-30s for APIs). [src2]
- **Using REST API v1 when HTTP API v2 suffices**: REST API costs $3.50/million requests vs $1.00/million for HTTP API. Fix: use `HttpApi` event type unless you need API keys, usage plans, or request validation. [src3]
- **Recursive Lambda invocations**: Lambda writing to S3 triggers another Lambda on the same bucket prefix. Fix: use separate prefixes or add idempotency checks. [src7]
- **Over-permissive IAM policies**: Using `Statement: Effect: Allow, Action: '*', Resource: '*'`. Fix: use SAM policy templates like `DynamoDBCrudPolicy`, `S3ReadPolicy`. [src6]

## Diagnostic Commands

```bash
# Validate SAM template
sam validate --lint

# List deployed stacks
aws cloudformation list-stacks --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE

# View function logs (tail mode)
sam logs --name GetItemsFunction --stack-name my-api --tail

# Check function configuration
aws lambda get-function-configuration --function-name my-api-GetItemsFunction-abc123

# Test local invocation with event
sam local invoke GetItemsFunction --event events/get.json --debug

# View API Gateway endpoints
aws apigatewayv2 get-apis --query 'Items[?Name==`my-api`]'

# Check deployment status
aws cloudformation describe-stack-events --stack-name my-api --max-items 10
```

## Version History & Compatibility

| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| SAM CLI 1.100+ | Current | Unified build behavior | Use `--build-in-source` for legacy behavior |
| SAM CLI 1.80–1.99 | Supported | `sam pipeline` GA | Pipeline bootstrap required re-run |
| Python 3.12 | Current | None | Recommended runtime |
| Python 3.9–3.11 | Supported | — | 3.8 EOL Oct 2024 |
| Node.js 22.x | Current | ESM default | Use `type: module` in package.json |
| Node.js 20.x | LTS until Apr 2026 | — | AWS SDK v3 required (v2 removed) |
| HTTP API (v2) | Current | — | Preferred for new projects |
| REST API (v1) | Stable | — | Use when v1-only features needed |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Building REST/HTTP APIs with <15 min execution | Long-running tasks >15 min | ECS Fargate, Step Functions |
| Event-driven processing (S3, SQS, DynamoDB Streams) | Sustained high throughput (>1000 req/s constant) | ECS/EKS with ALB |
| Prototyping and MVPs needing fast deployment | Complex stateful applications | EC2, ECS with persistent storage |
| Infrequent or bursty workloads | GPU/ML inference workloads | SageMaker endpoints |
| Team already uses CloudFormation | Team prefers Terraform or Pulumi | Terraform AWS provider |

## Important Caveats

- SAM is a superset of CloudFormation — any valid CloudFormation resource works in a SAM template, but SAM resource types (AWS::Serverless::*) are transformed at deploy time.
- `sam local` uses Docker containers that approximate but don't perfectly replicate the Lambda execution environment — always test in a dev stage before production.
- Lambda@Edge and CloudFront Functions are NOT supported via SAM's `AWS::Serverless::Function` — use raw CloudFormation resources for those.
- API Gateway throttling defaults (10,000 req/s burst, 5,000 req/s sustained) are account-level — one API can starve another.

## Related Units

- [Terraform AWS Basic Infrastructure](/software/devops/terraform-aws-basic/2026)
- [Cloudflare Workers Setup Reference](/software/devops/cloudflare-workers-setup/2026)
