Back to Blog

AWS API Gateway Patterns and Best Practices

September 14, 2025
11 min read
AWSAPI GatewayRESTServerlessArchitecture

AWS API Gateway is the front door to your serverless applications, handling millions of API calls while providing authentication, rate limiting, monitoring, and more. However, to build robust, scalable APIs, you need to understand the key patterns and best practices that separate production-ready APIs from basic implementations.

In this comprehensive guide, we'll explore proven API Gateway patterns, authentication strategies, performance optimizations, and monitoring approaches that will help you build APIs that scale.

API Gateway Types and When to Use Each

REST API vs HTTP API vs WebSocket API

Understanding which API Gateway type to use is crucial for your architecture:

When to Use Each Type:

  • REST API: Full feature set, request/response transformations, detailed monitoring
  • HTTP API: Lower cost, better performance, JWT authorizers, OIDC/OAuth2
  • WebSocket API: Real-time bidirectional communication, chat applications, live updates

Authentication and Authorization Patterns

JWT Authorizer with Cognito

The most common pattern for modern web applications combines Cognito User Pools with JWT authorizers:

jwt-authorizer.yaml
Resources:
  HttpApi:
    Type: AWS::ApiGatewayV2::Api
    Properties:
      Name: MyHttpApi
      ProtocolType: HTTP
      CorsConfiguration:
        AllowOrigins:
          - "https://myapp.com"
        AllowMethods:
          - GET
          - POST
          - PUT
          - DELETE
        AllowHeaders:
          - Authorization
          - Content-Type

  JWTAuthorizer:
    Type: AWS::ApiGatewayV2::Authorizer
    Properties:
      ApiId: !Ref HttpApi
      AuthorizerType: JWT
      IdentitySource:
        - $request.header.Authorization
      JwtConfiguration:
        Audience:
          - !Ref UserPoolClient
        Issuer: !Sub 'https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}'

Lambda Authorizer for Custom Logic

When you need custom authentication logic, Lambda authorizers provide maximum flexibility:

lambda-authorizer.js
exports.handler = async (event) => {
  const token = event.authorizationToken;
  
  try {
    // Custom token validation logic
    const user = await validateToken(token);
    
    // Generate policy
    const policy = generatePolicy(user.userId, 'Allow', event.methodArn);
    
    // Add user context
    policy.context = {
      userId: user.userId,
      email: user.email,
      role: user.role
    };
    
    return policy;
  } catch (error) {
    // Return 401 Unauthorized
    throw new Error('Unauthorized');
  }
};

function generatePolicy(principalId, effect, resource) {
  return {
    principalId,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: effect,
          Resource: resource
        }
      ]
    }
  };
}

Request/Response Transformation Patterns

Input Validation with Request Models

Always validate incoming requests at the gateway level to protect your backend services:

request-model.json
{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "User Registration Model",
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "format": "email",
      "description": "User email address"
    },
    "password": {
      "type": "string",
      "minLength": 8,
      "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]",
      "description": "Password with complexity requirements"
    },
    "firstName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    },
    "lastName": {
      "type": "string",
      "minLength": 1,
      "maxLength": 50
    }
  },
  "required": ["email", "password", "firstName", "lastName"],
  "additionalProperties": false
}

Response Transformation for Consistent APIs

Use mapping templates to ensure consistent response formats across your API:

response-template.vtl
{
  "success": true,
  "data": $input.json('$'),
  "metadata": {
    "requestId": "$context.requestId",
    "timestamp": "$context.requestTime",
    "version": "v1"
  }
}

## Error response template
#if($context.error)
{
  "success": false,
  "error": {
    "message": "$context.error.message",
    "type": "$context.error.messageString"
  },
  "metadata": {
    "requestId": "$context.requestId",
    "timestamp": "$context.requestTime"
  }
}
#end

Rate Limiting and Throttling Strategies

Usage Plans and API Keys

Implement tiered access with usage plans to monetize your API or provide different service levels:

usage-plans.yaml
Resources:
  # Basic tier - 1000 requests/day, 10 req/sec burst
  BasicUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      UsagePlanName: Basic
      Description: Basic tier with limited access
      Throttle:
        RateLimit: 5
        BurstLimit: 10
      Quota:
        Limit: 1000
        Period: DAY
      ApiStages:
        - ApiId: !Ref MyApi
          Stage: prod

  # Premium tier - 10000 requests/day, 100 req/sec burst
  PremiumUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      UsagePlanName: Premium
      Description: Premium tier with higher limits
      Throttle:
        RateLimit: 50
        BurstLimit: 100
      Quota:
        Limit: 10000
        Period: DAY
      ApiStages:
        - ApiId: !Ref MyApi
          Stage: prod

Method-Level Throttling

Apply different rate limits to different endpoints based on their resource requirements:

  • GET endpoints: Higher rate limits (100+ req/sec)
  • POST/PUT endpoints: Moderate limits (20-50 req/sec)
  • Resource-intensive operations: Lower limits (5-10 req/sec)
  • Authentication endpoints: Very low limits (1-2 req/sec)

Error Handling and Circuit Breaker Patterns

Gateway-Level Error Responses

Configure consistent error responses at the gateway level:

gateway-responses.yaml
Resources:
  # 429 Too Many Requests
  ThrottledResponse:
    Type: AWS::ApiGateway::GatewayResponse
    Properties:
      RestApiId: !Ref MyApi
      ResponseType: THROTTLED
      StatusCode: '429'
      ResponseTemplates:
        application/json: |
          {
            "error": {
              "code": "RATE_LIMIT_EXCEEDED",
              "message": "Too many requests. Please try again later.",
              "retryAfter": $context.error.responseOverride.header.Retry-After
            },
            "requestId": "$context.requestId"
          }

  # 401 Unauthorized
  UnauthorizedResponse:
    Type: AWS::ApiGateway::GatewayResponse
    Properties:
      RestApiId: !Ref MyApi
      ResponseType: UNAUTHORIZED
      StatusCode: '401'
      ResponseTemplates:
        application/json: |
          {
            "error": {
              "code": "UNAUTHORIZED",
              "message": "Invalid or missing authentication credentials"
            },
            "requestId": "$context.requestId"
          }

Monitoring and Observability

Essential CloudWatch Metrics

Monitor these key metrics to ensure API health:

Key Metrics to Track:

  • 4XXError: Client errors (authentication, validation)
  • 5XXError: Server errors (backend failures)
  • Latency: Response time distribution
  • Count: Total number of API calls
  • CacheHitCount/CacheMissCount: Caching effectiveness
  • IntegrationLatency: Backend response time

X-Ray Tracing for Deep Insights

Enable X-Ray tracing to understand request flows and identify bottlenecks:

xray-config.yaml
Resources:
  MyApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: MyAPI
      TracingConfig:
        TracingEnabled: true

  MyStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      RestApiId: !Ref MyApi
      StageName: prod
      TracingConfig:
        TracingEnabled: true
      AccessLogSetting:
        DestinationArn: !GetAtt ApiLogGroup.Arn
        Format: |
          {
            "requestId": "$context.requestId",
            "ip": "$context.identity.sourceIp",
            "caller": "$context.identity.caller",
            "user": "$context.identity.user",
            "requestTime": "$context.requestTime",
            "httpMethod": "$context.httpMethod",
            "resourcePath": "$context.resourcePath",
            "status": "$context.status",
            "protocol": "$context.protocol",
            "responseLength": "$context.responseLength"
          }

Performance Optimization Patterns

Caching Strategies

Implement intelligent caching to reduce backend load and improve response times:

  • Enable caching per method: Different TTLs for different endpoints
  • Cache key parameters: Include query strings and headers in cache keys
  • Cache invalidation: Implement cache warming and invalidation strategies
  • Conditional caching: Use cache only for GET requests with specific conditions

Connection Optimization

Configure VPC Links and private integrations for better performance and security:

vpc-link.yaml
Resources:
  VpcLink:
    Type: AWS::ApiGateway::VpcLink
    Properties:
      Name: MyVpcLink
      TargetArns:
        - !Ref NetworkLoadBalancer

  PrivateIntegration:
    Type: AWS::ApiGateway::Method
    Properties:
      RestApiId: !Ref MyApi
      ResourceId: !Ref MyResource
      HttpMethod: GET
      AuthorizationType: AWS_IAM
      Integration:
        Type: HTTP_PROXY
        IntegrationHttpMethod: GET
        Uri: http://internal-service.local/api
        ConnectionType: VPC_LINK
        ConnectionId: !Ref VpcLink

Security Best Practices

API Security Checklist

  • Enable CORS properly: Configure specific origins, not wildcards
  • Use HTTPS only: Redirect HTTP to HTTPS
  • Implement proper authentication: JWT, API keys, or IAM
  • Validate all inputs: Use request models and mapping templates
  • Rate limiting: Protect against abuse and DDoS
  • Resource-based policies: Control access at the API level
  • WAF integration: Protect against common web exploits

Conclusion

AWS API Gateway is a powerful service that can handle the complex requirements of modern APIs when configured correctly. By implementing these patterns and best practices, you'll build APIs that are secure, performant, and maintainable.

Start with basic authentication and rate limiting, then gradually add more sophisticated patterns like caching, monitoring, and advanced security features. Remember that a well-designed API Gateway configuration can significantly reduce the complexity of your backend services.

What API Gateway patterns have been most valuable in your projects? Share your experiences with authentication, rate limiting, or monitoring strategies!