AWS Lambda + API Gateway Reference (SAM Template)

Type: Software Reference Confidence: 0.93 Sources: 7 Verified: 2026-02-28 Freshness: 2026-02-28

TL;DR

Constraints

Quick Reference

SettingValue / DefaultNotes
Runtimepython3.12, nodejs20.x, nodejs22.x, java21python3.8–3.11 deprecated 2024+
Handlerapp.lambda_handler (Python), app.handler (Node)Relative to CodeUri
CodeUri./src/Path to function code
MemorySize128 MB (default), max 10240 MBCPU scales linearly with memory
Timeout3 sec (default), max 900 secSet realistically
Architecturesx86_64 (default) or arm64arm64 (Graviton2) is 20% cheaper
TracingActive or PassThroughActive enables X-Ray
AutoPublishAliasliveEnables gradual deploy
Environment.Variableskey-value mapUse SSM refs for secrets
Layerslist of Layer ARNsMax 5 layers per function
Events.TypeApi (REST v1) or HttpApi (v2)HTTP API is cheaper and faster
Events.Path/items/{id}Supports path parameters
Events.Methodget, post, put, deleteCase-insensitive
Globals.FunctionShared defaultsOverride 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]

sam init --runtime python3.12 --app-template hello-world --name my-api
cd my-api

Verify: ls template.yaml → expected: 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]

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

Parameters:
  Stage:
    Type: String
    Default: dev
    AllowedValues: [dev, staging, prod]

Resources:
  GetItemsFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.get_items
      Events:
        GetItems:
          Type: HttpApi
          Properties:
            Path: /items
            Method: get

  CreateItemFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.create_item
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref ItemsTable
      Events:
        CreateItem:
          Type: HttpApi
          Properties:
            Path: /items
            Method: post

  ItemsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH

Verify: sam validate → expected: template.yaml is a valid SAM Template

3. Build the application

Compile dependencies and prepare deployment artifacts. [src4]

# Standard build
sam build

# For native dependencies:
sam build --use-container

Verify: ls .aws-sam/build/ → expected: one directory per function

4. Test locally

Run the API locally before deploying. [src1]

sam local start-api --warm-containers EAGER
# In another terminal:
curl http://127.0.0.1:3000/items

Verify: curl http://127.0.0.1:3000/items → expected: JSON response

5. Deploy to AWS

Deploy with guided mode on first run. [src5]

# First deploy
sam deploy --guided

# Subsequent deploys
sam deploy

Verify: aws cloudformation describe-stacks --stack-name my-api --query 'Stacks[0].Outputs' → expected: ApiUrl output

Code Examples

Python: Lambda with API Gateway proxy integration

# 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']

    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

// 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({});
const ddb = DynamoDBDocumentClient.from(client);

exports.handler = async (event) => {
  const id = event.pathParameters?.id;
  if (!id) {
    return { statusCode: 400, body: JSON.stringify({ error: 'Missing id' }) };
  }
  try {
    const { Item } = await ddb.send(new GetCommand({
      TableName: process.env.TABLE_NAME,
      Key: { id }
    }));
    if (!Item) {
      return { statusCode: 404, body: JSON.stringify({ error: 'Not found' }) };
    }
    return { statusCode: 200, body: JSON.stringify(Item) };
  } catch (err) {
    console.error('DDB error:', JSON.stringify({ error: err.message, id }));
    return { statusCode: 500, body: JSON.stringify({ error: 'Internal error' }) };
  }
};

Anti-Patterns

Wrong: Monolithic Lambda (Lambdalith)

# ❌ BAD — single function handles all routes, over-permissioned
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)
    # 50 more routes...

Correct: One function per route group

# ✅ GOOD — separate functions with targeted permissions
Resources:
  UsersFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: users.handler
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
      Events:
        GetUsers:
          Type: HttpApi
          Properties: { Path: /users, Method: get }

Wrong: Hardcoded secrets in template.yaml

# ❌ BAD — secrets visible in CloudFormation and version control
Environment:
  Variables:
    DB_PASSWORD: "my-secret-password-123"

Correct: Use SSM Parameter Store references

# ✅ GOOD — resolved at deploy time, not stored in template
Environment:
  Variables:
    DB_PASSWORD: '{{resolve:ssm-secure:/myapp/db-password}}'

Wrong: No Globals section

# ❌ BAD — duplicating config across every function
Resources:
  Func1:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.12
      Timeout: 30
      MemorySize: 256
      # ... repeated for every function

Correct: Use Globals for shared defaults

# ✅ GOOD — shared config in one place
Globals:
  Function:
    Runtime: python3.12
    Timeout: 30
    MemorySize: 256
    Architectures: [arm64]

Common Pitfalls

Diagnostic Commands

# Validate SAM template
sam validate --lint

# 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
sam local invoke GetItemsFunction --event events/get.json --debug

# Check deployment status
aws cloudformation describe-stack-events --stack-name my-api --max-items 10

Version History & Compatibility

VersionStatusBreaking ChangesMigration Notes
SAM CLI 1.100+CurrentUnified build behaviorUse --build-in-source for legacy
Python 3.12CurrentNoneRecommended runtime
Node.js 22.xCurrentESM defaultUse type: module in package.json
Node.js 20.xLTS until Apr 2026AWS SDK v3 required
HTTP API (v2)CurrentPreferred for new projects
REST API (v1)StableUse when v1-only features needed

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Building REST/HTTP APIs with <15 min executionLong-running tasks >15 minECS Fargate, Step Functions
Event-driven processing (S3, SQS, DynamoDB Streams)Sustained high throughput (>1000 req/s constant)ECS/EKS with ALB
Prototyping and MVPsComplex stateful applicationsEC2, ECS with persistent storage
Infrequent or bursty workloadsGPU/ML inference workloadsSageMaker endpoints
Team already uses CloudFormationTeam prefers TerraformTerraform AWS provider

Important Caveats

Related Units