API Gateway turns Lambda functions into HTTP endpoints — the standard pattern for serverless REST APIs on AWS.

Architecture

  Client → API Gateway → Lambda → DynamoDB / S3 / etc.
                ↓
           CloudWatch Logs
  

Lambda Handler for HTTP

API Gateway sends events in a specific format:

  import json

def lambda_handler(event, context):
    http_method = event["httpMethod"]
    path = event["path"]
    query = event.get("queryStringParameters") or {}
    body = json.loads(event.get("body") or "{}")

    if http_method == "GET" and path == "/items":
        items = get_all_items()
        return response(200, items)

    if http_method == "GET" and path.startswith("/items/"):
        item_id = path.split("/")[-1]
        item = get_item(item_id)
        if not item:
            return response(404, {"error": "Not found"})
        return response(200, item)

    if http_method == "POST" and path == "/items":
        item = create_item(body)
        return response(201, item)

    if http_method == "DELETE" and path.startswith("/items/"):
        item_id = path.split("/")[-1]
        delete_item(item_id)
        return response(204, None)

    return response(404, {"error": "Route not found"})

def response(status_code, body):
    return {
        "statusCode": status_code,
        "headers": {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*",
        },
        "body": json.dumps(body) if body is not None else "",
    }
  

Deploy with AWS SAM

AWS SAM simplifies API + Lambda deployment:

  # template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  ItemsApi:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/
      Handler: app.lambda_handler
      Runtime: python3.12
      MemorySize: 256
      Timeout: 30
      Events:
        GetItems:
          Type: Api
          Properties:
            Path: /items
            Method: GET
        CreateItem:
          Type: Api
          Properties:
            Path: /items
            Method: POST
        GetItem:
          Type: Api
          Properties:
            Path: /items/{id}
            Method: GET
        DeleteItem:
          Type: Api
          Properties:
            Path: /items/{id}
            Method: DELETE
  
  sam build
sam deploy --guided
# Outputs: API endpoint URL
  

CORS Configuration

For browser clients, handle OPTIONS preflight:

  def lambda_handler(event, context):
    if event["httpMethod"] == "OPTIONS":
        return {
            "statusCode": 200,
            "headers": {
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Methods": "GET,POST,DELETE,OPTIONS",
                "Access-Control-Allow-Headers": "Content-Type,Authorization",
            },
            "body": "",
        }
    ...
  

Or configure CORS in API Gateway / SAM template:

  Events:
  GetItems:
    Type: Api
    Properties:
      Path: /items
      Method: GET
      RestApiId: !Ref ServerlessApi

# Add to API resource:
# Cors:
#   AllowMethods: "'GET,POST,DELETE,OPTIONS'"
#   AllowHeaders: "'Content-Type,Authorization'"
#   AllowOrigin: "'*'"
  

Authentication with Cognito / JWT

  def lambda_handler(event, context):
    # API Gateway validates JWT and passes claims
    claims = event.get("requestContext", {}).get("authorizer", {}).get("claims", {})
    user_id = claims.get("sub")

    if not user_id:
        return response(401, {"error": "Unauthorized"})
    ...
  

Configure a Cognito User Pool authorizer in API Gateway for automatic JWT validation.

DynamoDB Integration

  import boto3
from decimal import Decimal
import json

dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("Items")

class DecimalEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)

def get_all_items():
    return table.scan()["Items"]

def create_item(data):
    import uuid
    item = {"id": str(uuid.uuid4()), **data}
    table.put_item(Item=item)
    return item
  

Testing Locally

  sam local start-api
curl http://127.0.0.1:3000/items
curl -X POST http://127.0.0.1:3000/items -d '{"name": "Test"}'
  

API Gateway vs Function URL

Feature API Gateway Lambda Function URL
Routing Path/method based Single endpoint
Auth Cognito, IAM, API keys IAM, none
Rate limiting Built-in None
Caching Yes No
Cost Per request Free (Lambda only)

Use API Gateway for production REST APIs. Function URLs for simple webhooks.

API Gateway + Lambda is the most common serverless API pattern on AWS.