Amazon API Gateway

A fully managed "front door" for APIs — it terminates client requests, authenticates and authorizes them, optionally transforms and validates the payload, invokes a backend integration (Lambda, another AWS service, or an HTTP endpoint), and shapes the response — all without you running or scaling a single server. On the DVA-C02 exam, API Gateway questions almost always hinge on picking the right API type, the right integration mode, or the right authorizer for a described scenario, and spotting the classic proxy-vs-non-proxy and CORS traps.

Fully managed API front door REST API vs HTTP API Serverless-native (pairs with Lambda)
29 sec
Max integration timeout (all API types)
10 MB
Max payload size (request/response)
2
Primary API types — REST & HTTP
3
Authorizer families — Cognito, IAM, Lambda

Why API Gateway Exists

The Problem It Solves

Core Components at a Glance

⚠️ The Recurring Exam Theme

Nearly every API Gateway question tests one of four things: (1) can you choose REST API vs HTTP API based on described feature/cost/latency requirements, (2) do you understand the proxy vs non-proxy Lambda integration distinction — proxy passes the whole request through untouched, non-proxy needs mapping templates, (3) can you pick the right authorizer for a described identity/auth requirement, and (4) can you diagnose that a CORS error is actually a missing response header from the backend, not just a checkbox in API Gateway.

How a Request Actually Flows Through API Gateway

1. Client Request

HTTPS request hits the API's invoke URL — either the default https://{api-id}.execute-api.eu-west-1.amazonaws.com/{stage} or a custom domain — targeting a resource/method (REST) or route (HTTP)

2. Stage Resolution

API Gateway resolves which deployed stage config applies — throttling limits, stage variables, caching settings, logging level for that stage

3. Authorization (if configured)

Cognito User Pool authorizer validates a JWT, IAM auth validates a SigV4-signed request, or a Lambda authorizer runs custom logic. Unauthorized requests are rejected here with 401/403 — the backend integration is never invoked

4. Request Validation / Throttling (if configured)

Method/route-level throttling (token bucket: rate + burst) rejects excess traffic with 429. Request validation checks required parameters/headers or a JSON schema and rejects malformed input with 400 — before the integration runs

5. Integration Invoked

Lambda proxy (raw event passed as-is) or Lambda non-proxy (request mapping template/VTL transforms the payload first), an HTTP backend, a direct AWS service integration, or a Mock integration

Lambda proxyLambda non-proxy + VTLHTTP integrationAWS service integrationMock
6. Response Shaping & Return

Non-proxy integrations pass the backend response through a response mapping template; proxy integrations return the Lambda-formatted response near-verbatim (status code, headers, body). Caching (REST only) may serve the response from cache within its TTL

7. Observability

Access logs and execution logs stream to CloudWatch Logs; if X-Ray tracing is enabled on the stage, the request/response is captured as a segment in the trace and service map

Exam Domain Mapping

DVA-C02 DomainWhere API Gateway Shows Up
Domain 1 — Development with AWS ServicesThe centerpiece — choosing REST vs HTTP API, integration types, mapping templates, request validation, direct AWS service integrations
Domain 2 — SecurityAuthorizer selection (Cognito/IAM/Lambda), API keys/usage plans, resource policies, CORS as a security-adjacent misconfiguration
Domain 3 — DeploymentStages, deployments, canary release deployments, custom domains + base path mapping, stage variables driving which Lambda alias/version is invoked
Domain 4 — Troubleshooting and OptimizationDiagnosing 429 throttling errors, 403 authorizer failures, CORS errors, latency issues (caching, integration timeout), reading CloudWatch/X-Ray for root cause

API Gateway is one of the most consistently tested services on DVA-C02 because it sits at the intersection of all four domains — expect several questions built around it directly, plus many more where it's one component of a larger serverless architecture question.

Final Summary

Must Memorize
  • Lambda proxy vs non-proxy integration — proxy = no mapping template, raw event; non-proxy = mapping templates required
  • REST-API-only features: API keys, usage plans, caching, canary deployments, private (VPC endpoint) APIs
  • Max integration timeout is 29 seconds, max payload 10 MB — for every API type
  • Authorizer types: Cognito User Pool, IAM (SigV4), Lambda (token vs request)
  • CORS errors are frequently a missing Access-Control-Allow-Origin response header from the backend, not an API Gateway setting
Must Understand
  • Stages/deployments — no deployment, no visible change, even after saving config
  • Throttling burst vs rate (token bucket) at account, stage, and method/route level
  • VTL mapping templates transform request and response bodies for non-proxy integrations
  • Custom domain + base path mapping routes a friendly hostname/path to a specific API + stage
  • Stage variables commonly point at a Lambda alias so a stage can target a specific function version
Can De-prioritize
  • Exact historical per-request pricing figures
  • Console click-path specifics
  • SDK generation / OpenAPI export mechanics

Exam appearance probability: HIGH

Components — Deep Dive

The configuration surface that shows up repeatedly in scenario questions: how requests are structured, how they reach a backend, who's allowed to call them, and how traffic is controlled.

1.1 Resources & Methods (REST) vs Routes (HTTP)
REST APIResources form a URL tree (/orders, /orders/{id}); each resource has one or more Methods (GET/POST/PUT/DELETE/ANY)
HTTP APIA flat list of Routes, each a METHOD /path pair (e.g. GET /orders/{id}), with an optional catch-all $default route/integration
1.2 Stages & Deployments High-trap
StageA named, addressable snapshot (e.g. dev, test, prod) with its own throttling, caching, logging, and stage variables
DeploymentThe action that publishes the current resource/method/integration configuration to a stage
1.3 Lambda Integrations — Proxy vs Non-Proxy Classic trap
Lambda Proxy IntegrationThe entire HTTP request (headers, query string, path params, body, request context) is passed to Lambda as a single event object; Lambda's response must be a specific shape
Lambda Non-Proxy (Custom) IntegrationAPI Gateway uses a request mapping template (VTL) to transform the incoming request into whatever shape you define before invoking Lambda, and a response mapping template to transform Lambda's raw output back into the client-facing response
⚠️ The exam trap

A question describing "the Lambda function needs full access to headers, query parameters, and the HTTP method" is testing proxy integration. A question describing "the request body must be transformed into a different schema before it reaches a legacy-style Lambda function, without changing the Lambda function's code" is testing non-proxy integration with mapping templates.

1.4 Other Integration Types
⚠️ Exam angle

"Reduce operational overhead by removing the Lambda function entirely for a simple write-to-DynamoDB API" → AWS service integration (direct DynamoDB integration), not a Lambda proxy. This is a frequent "LEAST operational overhead" pattern question.

1.5 Authorizers
Cognito User Pool AuthorizerValidates a JWT (ID or access token) issued by a Cognito User Pool; API Gateway checks signature/expiry itself — no Lambda invoked
IAM Authorization (SigV4)Caller signs the request with AWS credentials; API Gateway checks the signature against IAM policy — used for service-to-service or same-account/trusted-caller scenarios
Lambda Authorizer — Token typeReceives just a bearer token (e.g. from the Authorization header) and returns an IAM policy document + optional context
Lambda Authorizer — Request typeReceives the full request (headers, query string params, path params, source IP, stage variables) — needed when the authorization decision depends on more than a single token
1.6 API Keys, Usage Plans & Throttling REST API only
API KeyAn identifier (not an authentication mechanism) that a client includes in the x-api-key header — used for metering/throttling, not for verifying identity
Usage PlanTies one or more API keys to throttle limits and a request quota (e.g. 1,000,000 requests/month) across one or more APIs/stages
Throttling — RateSustained steady-state requests per second allowed
Throttling — BurstToken-bucket capacity for short spikes above the steady-state rate
1.7 Caching REST API only
1.8 CORS Classic trap
⚠️ The exam trap

"Enabled CORS in the console, preflight (OPTIONS) succeeds, but the browser still blocks the actual GET/POST request with a CORS error" is testing whether you know the real GET/POST response — typically produced by a Lambda proxy integration — is missing Access-Control-Allow-Origin in its own headers. The fix is in the Lambda function's response, not another look at the API Gateway CORS checkbox.

1.9 Custom Domains & Base Path Mapping
1.10 Mapping Templates (VTL) & Request Validation
1.11 Monitoring

AWS Exam Thinking

Requirement → Keywords → Expected Answer → why every distractor fails.

Lowest-cost, lowest-latency serverless API with JWT auth via Cognito
cost-optimizedlow latencyJWT / OIDC
Expected Answer

HTTP API with a JWT authorizer

DistractorWhy it's wrong
REST API with Cognito User Pool authorizerFunctionally works but costs more and adds latency versus HTTP API — wrong when the requirement explicitly asks for lowest cost/latency and none of the REST-only features (usage plans, caching, canary) are needed
Application Load Balancer with Lambda targetsALB can invoke Lambda but lacks native JWT/OIDC authorizer support and API-specific features like route-based path matching the way API Gateway offers
REST API with a Lambda authorizerAdds an unnecessary Lambda invocation and REST API's higher cost when HTTP API's built-in JWT authorizer already covers standards-based JWT validation natively
Need API keys, usage plans, caching, and request validation together
usage plansAPI keyscaching
Expected Answer

REST API

DistractorWhy it's wrong
HTTP APIDoes not support usage plans, API keys, response caching, or full JSON Schema request validation — all explicitly required here
ALB + LambdaNo native concept of API keys, usage plans, or response caching at all
CloudFront + Lambda@EdgeCloudFront caches, but has no usage-plan/API-key/request-validation concept — solving a different layer of the problem
Lambda function needs full HTTP request detail with minimal API Gateway config
headersquery paramswhole request
Expected Answer

Lambda proxy integration

DistractorWhy it's wrong
Lambda non-proxy integrationWould require building and maintaining a mapping template to manually forward every header/param the function needs — unnecessary extra work when proxy already passes everything
HTTP integrationRoutes to an HTTP endpoint, not a Lambda function invocation
Mock integrationNever calls a backend at all
Transform request payload to match a legacy backend schema, without changing app code
payload transformationlegacy schema
Expected Answer

Lambda non-proxy (custom) integration with a request mapping template (VTL)

DistractorWhy it's wrong
Lambda proxy integrationPasses the request through unmodified — the transformation the requirement asks for would have to happen inside the function, contradicting "without changing app code"
Add a second Lambda function as a transformation layerWorks but is unnecessary extra infrastructure and cost when a mapping template does this natively at the gateway
Request validationRejects malformed requests; it doesn't transform valid ones into a different shape
Authenticate internal service-to-service calls using existing AWS credentials
service-to-serviceAWS credentialsSigV4
Expected Answer

IAM authorization (SigV4-signed requests)

DistractorWhy it's wrong
Cognito User Pool authorizerBuilt for end-user identity via a Cognito user directory, not for AWS-native service-to-service calls that already carry IAM credentials
API keysAPI keys identify a caller for throttling/billing, they are not an authentication mechanism and don't verify AWS identity
Lambda authorizerAdds custom logic and a Lambda invocation to solve something IAM auth already handles natively via existing IAM roles/policies
Custom auth logic validating a JWT from a third-party (non-Cognito) identity provider
third-party IdPcustom token validation
Expected Answer

Lambda authorizer — TOKEN type

DistractorWhy it's wrong
Cognito User Pool authorizerOnly validates tokens issued by a Cognito User Pool (or a Cognito-federated identity), not an arbitrary third-party IdP's JWT structure
Lambda authorizer — REQUEST typeOverkill when the decision only needs the bearer token itself, not the full request context — token type is the more precise/simpler fit
IAM authorizationValidates AWS SigV4 signatures, not arbitrary third-party JWTs
Authorization decision depends on multiple request inputs (headers + query params + source IP)
multiple inputsnot just a token
Expected Answer

Lambda authorizer — REQUEST type

DistractorWhy it's wrong
Lambda authorizer — TOKEN typeOnly receives the bearer token itself — cannot see headers, query params, or source IP needed for this decision
Cognito User Pool authorizerPurpose-built for validating a Cognito JWT, not for arbitrary multi-input custom logic
Resource policyCan restrict by source IP/VPC/AWS account at the API level, but can't run custom application logic combining multiple request attributes
CORS error persists in browser despite CORS being "enabled" in API Gateway
CORS errorLambda proxypreflight succeeds
Expected Answer

Add Access-Control-Allow-Origin (and related) headers to the Lambda function's actual response

DistractorWhy it's wrong
Re-enable CORS in the API Gateway console againEnabling CORS only configures the auto-generated OPTIONS preflight response — it does not retroactively add headers to a Lambda proxy integration's real GET/POST response
Switch to a non-proxy integrationWould work if you also build a response mapping template to inject the headers, but is unnecessary rearchitecting versus simply fixing the Lambda response — the proxy model is not itself the problem
Move the API behind CloudFrontDoes not add missing CORS headers to the origin response; the browser still evaluates headers from the actual response received
Per-customer throttling limits and usage quota tracking for a partner API
per-customer limitsquota tracking
Expected Answer

API keys + usage plans (REST API)

DistractorWhy it's wrong
WAF rate-based ruleRate-limits by source IP pattern, not by a per-customer identifier tied to a metered quota
Account-level throttling onlyApplies uniformly across the whole account/region — cannot differentiate limits or quotas between individual customers
HTTP APIHas no usage plan / API key concept at all — this requirement alone rules it out
Reduce latency for a GET endpoint returning largely static/slow-changing data
reduce latencyread-heavyrarely changes
Expected Answer

Enable API Gateway caching on that method (REST API)

DistractorWhy it's wrong
Increase Lambda memory/concurrencyAddresses backend compute speed, not the repeated round-trip to the backend for data that hasn't changed
Switch to HTTP API for lower baseline latencyHTTP API has slightly lower baseline overhead but has no caching feature at all — doesn't solve "avoid repeated backend calls for unchanged data" as directly as caching does
Add a Lambda authorizer cacheOnly caches the authorization decision, not the actual response payload
"LEAST operational overhead" write-to-DynamoDB API
least operational overheadsimple write
Expected Answer

API Gateway direct (AWS service) integration to DynamoDB

DistractorWhy it's wrong
Lambda proxy integration writing to DynamoDBIntroduces a Lambda function to maintain, patch, and pay for, purely to shuttle a request into DynamoDB — more operational overhead than needed
ALB + EC2 application writing to DynamoDBRequires managing servers/Auto Scaling — far more operational overhead than a direct managed integration
Step Functions Express Workflow triggered by API GatewayAdds orchestration machinery unnecessary for a single simple write

Integrations With Other AWS Services

AWS Lambda
WhatThe most common backend — invoked via proxy or non-proxy integration
WhyFully serverless request handling with no infrastructure to manage on either side
PatternClient → API Gateway (auth, throttling, validation) → Lambda → downstream data store
Amazon Cognito
WhatUser Pools issue JWTs validated by a Cognito User Pool authorizer (REST) or a JWT authorizer (HTTP API); Identity Pools exchange a validated identity for temporary AWS credentials
WhyTurns "who is calling this API" into a managed problem rather than something each Lambda function re-implements
PatternClient authenticates against a Cognito User Pool → receives ID/access token → API Gateway authorizer validates the token → request context (claims) passed to the integration

See the Cognito guide for the full User Pools vs Identity Pools breakdown and JWT structure (ID token vs access token vs refresh token).

AWS IAM
WhatSigV4 request signing for IAM authorization; execution roles granting API Gateway permission to call Lambda or other AWS services; resource policies restricting who/where can invoke the API
WhyNative AWS-to-AWS trust without managing a separate identity system, and fine-grained control over source VPC/account/IP for private or restricted APIs
PatternTrusted internal caller signs request with SigV4 → API Gateway validates signature against IAM policy → integration invoked with the caller's IAM identity available in the request context
Amazon DynamoDB / Amazon SQS / Amazon SNS / AWS Step Functions
WhatDirect AWS service integrations — API Gateway calls these services' APIs itself, using a mapping template to build the call
WhyRemoves a Lambda function from simple pass-through operations (write an item, enqueue a message, start a workflow), reducing cost and moving parts
PatternClient POST → API Gateway request mapping template builds a PutItem/SendMessage/StartExecution call → service invoked directly using the API Gateway execution role's IAM permissions
CloudWatch & AWS X-Ray
WhatMetrics, execution logs, and access logs flow to CloudWatch; distributed traces flow to X-Ray when enabled per stage
WhyRoot-cause latency and error diagnosis across the whole request path, not just inside one Lambda function
PatternRequest → API Gateway segment → Lambda segment → downstream service segment, all stitched into one X-Ray trace and visualized on the service map
Elastic Load Balancing / VPC Link
WhatVPC Link lets API Gateway (REST or HTTP) privately reach an internal ALB/NLB or Cloud Map service without exposing it publicly
WhyFront an existing internal service (containers on ECS/EKS, EC2 fleet) with API Gateway's auth, throttling, and observability without opening it to the internet directly
PatternClient → API Gateway (public) → VPC Link → internal NLB/ALB → ECS/EC2 backend (private subnet)

End-to-End Architecture Example: Serverless API With Authentication and Tracing

1. Sign-in

Mobile/web client authenticates against a Cognito User Pool (hosted UI or SDK), receiving an ID token and access token (JWT)

2. API Call

Client calls an HTTP API endpoint, e.g. GET /orders, with the access token in the Authorization header

3. Authorization

HTTP API's JWT authorizer validates the token's signature, issuer, and audience against the User Pool — rejects with 401 if invalid/expired, otherwise passes claims into the request context

4. Integration

Lambda proxy integration invokes a function that reads the caller's sub claim from the request context and queries DynamoDB for that user's orders

5. Observability

X-Ray tracing (enabled on the stage and inside the Lambda function) captures the full trace — API Gateway segment, Lambda segment, DynamoDB subsegment — visualized as a service map; CloudWatch dashboards/alarms watch 5XXError and Latency metrics

This exact pattern — API Gateway (HTTP API) → Lambda → DynamoDB with a Cognito authorizer and X-Ray tracing — is one of the canonical serverless architectures on the exam. See the Cross-Service Architectures guide for the fully worked version alongside four other end-to-end architectures.

Best Practices & Common Exam Traps

When to Use API Gateway — and When Not To

Use API Gateway When…
  • You need a managed HTTPS front door for Lambda, HTTP backends, or direct AWS service calls with zero server management
  • You need centralized auth (Cognito/IAM/Lambda authorizer), throttling, and/or request validation applied before backend code runs
  • You need per-customer API keys/usage plans, response caching, or a private (VPC-only) API — all REST-API-specific strengths
  • You want unified request/response logging and X-Ray tracing across heterogeneous backends
Consider Alternatives When…
  • Simple internal HTTP routing to containers/EC2 with no need for API-specific features → an Application Load Balancer alone is simpler and cheaper
  • GraphQL API with real-time subscriptions → AWS AppSync is purpose-built for GraphQL; API Gateway has no native GraphQL support
  • Static content / global edge caching of whole pages or assets → CloudFront (optionally with Lambda@Edge/CloudFront Functions) is the better fit; API Gateway caching is method-level, not a CDN
  • Long-running requests over 29 seconds → API Gateway cannot support this; use an asynchronous pattern (API Gateway kicks off Step Functions/SQS, client polls or receives a webhook/WebSocket push) instead of expecting a single synchronous call to wait longer

REST API vs HTTP API — The Core Comparison

FeatureREST APIHTTP API
Relative costBaseline (higher)~70% cheaper for equivalent traffic
LatencyBaselineLower — fewer processing steps
Lambda proxy integrationYesYes
Lambda non-proxy integration + VTL mapping templatesYesNo
API keys & usage plansYesNo
Response cachingYesNo
Request validation (JSON Schema)Yes, full JSON SchemaBasic parameter checks only
AuthorizersCognito User Pool, IAM, Lambda (token/request)Native JWT (OIDC/OAuth2), IAM, Lambda (simplified request format)
Private APIs (VPC endpoint + resource policy)YesNo (VPC Link for private integrations is supported; the API itself can't be made VPC-private the way REST APIs can)
Canary release deploymentsYesNo
Endpoint typesEdge-optimized, Regional, PrivateRegional only
AWS WAF integrationYes — Web ACL attaches directly to the APINo — HTTP APIs aren't a WAF-associable resource; front it with CloudFront and attach the Web ACL there instead
Automatic deploymentsManual deployment step requiredOptional auto-deploy on change
⚠️ Exam angle

If a scenario mentions any of: API keys, usage plans, caching, request body JSON Schema validation, canary deployments, or a private VPC-only API — REST API is required and HTTP API is automatically wrong, regardless of how strongly "lowest cost" is emphasized elsewhere in the question.

Common Exam Traps

MisconceptionReality
"Enabling CORS in API Gateway fixes all CORS errors"It only configures the auto-generated OPTIONS preflight response. The real GET/POST/etc. response — especially from a Lambda proxy integration — must itself include the CORS headers
"Lambda proxy integration passes the response through unmodified"API Gateway enforces a specific response shape (statusCode/headers/body); a bare returned object causes a 500 error, not a pass-through
"Non-proxy integration is simpler because there's less to configure in Lambda"It shifts complexity to API Gateway — you must write and maintain request AND response VTL mapping templates
"HTTP APIs support usage plans and API keys just like REST APIs"They don't — this is a hard, frequently tested feature gap
"Stage variables are just for display/documentation"They're live configuration values, commonly used to point a stage at a specific Lambda alias/version or a different backend endpoint per environment
"Saving a change to a method/integration makes it live immediately"Nothing takes effect for callers until you create a new Deployment to the target stage
"Throttling is a single account-wide number"It's layered — account, stage, and method/route level — with the most specific configured limit applying (never exceeding the account ceiling)
"API keys authenticate the caller"API keys identify a caller for metering/throttling; they are not proof of identity and provide no real access control on their own
"A custom domain automatically knows which API/stage to route to"You must explicitly create a base path mapping connecting the domain (and optional path) to a specific API and stage
"Caching guarantees fresh data disappears instantly on backend change"Cached responses persist until TTL expiry (default 300s, up to 3600s) or an explicit cache flush/invalidation

Memory Anchors

Say It Fast
  • Proxy = no mapping template. Non-proxy = mapping templates, both directions
  • REST = keys, plans, cache, canary, private. HTTP = cheap, fast, JWT-native
  • 29 seconds, 10 MB — every API type, no exceptions
Auth Recall
  • Cognito authorizer = validate a Cognito JWT
  • IAM/SigV4 = trusted AWS caller with a signed request
  • Lambda token = just the bearer token; Lambda request = the whole request
Troubleshooting Recall
  • CORS error but preflight passes → check the Lambda function's own response headers
  • Config change not reflected → check whether a new Deployment was made
  • 429 → throttling, check account/stage/method limits; 403 → authorizer/resource-policy denial

Hands-On Lab

Three self-contained exercises, all in eu-west-1, using AWS CLI v2. Exercise 1 builds an HTTP API with a Lambda proxy integration. Exercise 2 rebuilds the same conceptual endpoint on a REST API with a non-proxy integration and a real VTL mapping template, so you can see the proxy-vs-non-proxy trap from Components 1.3 happen on your own screen instead of just reading about it. Exercise 3 layers a usage plan and API key onto the REST API and deliberately triggers a 429. Replace <ACCOUNT_ID> with your account ID throughout.

Exercise 1 — HTTP API + Lambda Proxy Integration

  1. Create the IAM trust policy and execution role the Lambda function will run as.
    cat > trust-policy.json <<'EOF'
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": { "Service": "lambda.amazonaws.com" },
        "Action": "sts:AssumeRole"
      }]
    }
    EOF
    
    aws iam create-role \
      --role-name orders-proxy-lambda-role \
      --assume-role-policy-document file://trust-policy.json
    
    aws iam attach-role-policy \
      --role-name orders-proxy-lambda-role \
      --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
    CreateRole returns an Arn like arn:aws:iam::<ACCOUNT_ID>:role/orders-proxy-lambda-role; the policy attaches with no output.
  2. Write the Lambda handler. Because this will sit behind a proxy integration, it must return the full API Gateway proxy response shape itself — statusCode, headers, and a JSON-string body.
    cat > index.js <<'EOF'
    exports.handler = async (event) => {
      const id = event.pathParameters && event.pathParameters.id;
      return {
        statusCode: 200,
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          id: id,
          status: "CONFIRMED",
          source: "lambda-proxy",
          rawQueryString: event.rawQueryString || ""
        })
      };
    };
    EOF
    zip function-proxy.zip index.js
    function-proxy.zip is created in the current directory.
    ⚠️ Why this step matters for the exam

    This is the proxy contract tested repeatedly on DVA-C02: if this function returned a bare object like { id, status } instead of the statusCode/headers/body shape, API Gateway would not attempt to interpret it — it would return a 500 error to the client. Proxy integrations do zero response transformation.

  3. Create the function and grant it a Lambda alias-free deploy (using the $LATEST version is fine for this lab).
    aws lambda create-function \
      --region eu-west-1 \
      --function-name orders-proxy-fn \
      --runtime nodejs20.x \
      --handler index.handler \
      --role arn:aws:iam::<ACCOUNT_ID>:role/orders-proxy-lambda-role \
      --zip-file fileb://function-proxy.zip
    create-function returns FunctionArn: arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:orders-proxy-fn, State ACTIVE (or Pending briefly).
  4. Create the HTTP API itself (no target yet — the route and integration are wired up explicitly in the next steps).
    aws apigatewayv2 create-api \
      --region eu-west-1 \
      --name orders-http-api \
      --protocol-type HTTP
    Returns an ApiId (e.g. abc123xyz4) and an ApiEndpoint like https://abc123xyz4.execute-api.eu-west-1.amazonaws.com. Save the ApiId — it's needed in every following command.
  5. Create the Lambda proxy integration on the API, then a route that points at it.
    aws apigatewayv2 create-integration \
      --region eu-west-1 \
      --api-id <API_ID> \
      --integration-type AWS_PROXY \
      --integration-uri arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:orders-proxy-fn \
      --payload-format-version 2.0 \
      --integration-method POST
    
    aws apigatewayv2 create-route \
      --region eu-west-1 \
      --api-id <API_ID> \
      --route-key "GET /orders/{id}" \
      --target integrations/<INTEGRATION_ID>
    create-integration returns an IntegrationId to plug into --target as integrations/<INTEGRATION_ID>; create-route returns a RouteId.
  6. Let API Gateway invoke the function, then deploy a stage.
    aws lambda add-permission \
      --region eu-west-1 \
      --function-name orders-proxy-fn \
      --statement-id apigw-invoke-proxy \
      --action lambda:InvokeFunction \
      --principal apigateway.amazonaws.com \
      --source-arn "arn:aws:execute-api:eu-west-1:<ACCOUNT_ID>:<API_ID>/*/*/orders/*"
    
    aws apigatewayv2 create-stage \
      --region eu-west-1 \
      --api-id <API_ID> \
      --stage-name dev \
      --auto-deploy
    add-permission returns a Statement JSON blob; create-stage returns StageName "dev" with AutoDeploy true, so future changes go live without a manual deployment call.
  7. Test it.
    curl -s "https://<API_ID>.execute-api.eu-west-1.amazonaws.com/dev/orders/42?expand=items"
    {"id":"42","status":"CONFIRMED","source":"lambda-proxy","rawQueryString":"expand=items"} — the whole request (path param AND query string) reached the function untouched, with zero API Gateway configuration to parse them.

Exercise 2 — Same Endpoint, REST API + Lambda Non-Proxy Integration + VTL

Same conceptual GET /orders/{id} endpoint, rebuilt as a REST API with a non-proxy (custom) Lambda integration. This time API Gateway — not the function — is responsible for shaping both the request going in and the response coming out, via VTL mapping templates.

  1. Write a second Lambda function. Because it sits behind a non-proxy integration, it can return whatever bare shape is convenient — no statusCode/headers/body envelope required.
    cat > index.js <<'EOF'
    exports.handler = async (event) => {
      return {
        orderId: event.orderId,
        status: "CONFIRMED",
        filter: event.queryParams ? event.queryParams.expand : null
      };
    };
    EOF
    zip function-nonproxy.zip index.js
    
    aws lambda create-function \
      --region eu-west-1 \
      --function-name orders-nonproxy-fn \
      --runtime nodejs20.x \
      --handler index.handler \
      --role arn:aws:iam::<ACCOUNT_ID>:role/orders-proxy-lambda-role \
      --zip-file fileb://function-nonproxy.zip
    FunctionArn arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:orders-nonproxy-fn, State ACTIVE. Note the function never sees an "event" shaped like the raw HTTP request — it only sees whatever the request mapping template builds in the next steps.
  2. Create the REST API and the /orders/{id} resource tree.
    aws apigateway create-rest-api \
      --region eu-west-1 \
      --name orders-rest-api \
      --endpoint-configuration types=REGIONAL
    
    # Get the auto-created root resource id ("/")
    aws apigateway get-resources --region eu-west-1 --rest-api-id <REST_API_ID>
    
    aws apigateway create-resource \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --parent-id <ROOT_RESOURCE_ID> --path-part orders
    
    aws apigateway create-resource \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --parent-id <ORDERS_RESOURCE_ID> --path-part "{id}"
    Two new resources exist: /orders and /orders/{id}, each returned with its own ResourceId to use below.
  3. Add the GET method on /orders/{id} (no auth for this lab).
    aws apigateway put-method \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --resource-id <ID_RESOURCE_ID> --http-method GET \
      --authorization-type NONE \
      --request-parameters "method.request.path.id=true,method.request.querystring.expand=false"
    put-method returns the method definition with httpMethod GET and the declared request parameters.
  4. Write the request mapping template (VTL) that turns the raw HTTP request into the plain object the function expects, then wire up the non-proxy integration.
    cat > request-template.vtl <<'EOF'
    {
      "orderId": "$input.params('id')",
      "queryParams": {
        #foreach($p in $input.params().querystring.keySet())
        "$p": "$util.escapeJavaScript($input.params().querystring.get($p))"#if($foreach.hasNext),#end
        #end
      }
    }
    EOF
    
    aws apigateway put-integration \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --resource-id <ID_RESOURCE_ID> --http-method GET \
      --type AWS \
      --integration-http-method POST \
      --uri "arn:aws:apigateway:eu-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:eu-west-1:<ACCOUNT_ID>:function:orders-nonproxy-fn/invocations" \
      --request-templates file://request-template-wrapped.json
    put-integration returns type AWS (not AWS_PROXY) — confirming this is a custom, mapping-template-driven integration.
    ⚠️ Why this step matters for the exam

    --request-templates takes a JSON object mapping content-type → template string (so the .vtl file above must be wrapped as {"application/json": "<escaped template>"} before passing it as request-template-wrapped.json). This extra packaging step is exactly the "more setup" the guide's Components tab warns about for non-proxy — API Gateway, not your function, now owns the request shape.

  5. Write the response mapping template so the client sees a clean, gateway-defined response shape regardless of what the function returned.
    cat > response-template.vtl <<'EOF'
    #set($inputRoot = $input.path('$'))
    {
      "order": {
        "id": "$inputRoot.orderId",
        "status": "$inputRoot.status"
      },
      "source": "lambda-nonproxy-vtl"
    }
    EOF
    
    aws apigateway put-method-response \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --resource-id <ID_RESOURCE_ID> --http-method GET \
      --status-code 200
    
    aws apigateway put-integration-response \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --resource-id <ID_RESOURCE_ID> --http-method GET \
      --status-code 200 \
      --response-templates file://response-template-wrapped.json
    Both calls return 200 with the method/integration response objects echoed back — the pipeline from Lambda's raw return value to the client-facing JSON is now fully defined by API Gateway, not by the function.
  6. Grant invoke permission and deploy to a stage.
    aws lambda add-permission \
      --region eu-west-1 \
      --function-name orders-nonproxy-fn \
      --statement-id apigw-invoke-nonproxy \
      --action lambda:InvokeFunction \
      --principal apigateway.amazonaws.com \
      --source-arn "arn:aws:execute-api:eu-west-1:<ACCOUNT_ID>:<REST_API_ID>/*/GET/orders/*"
    
    aws apigateway create-deployment \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --stage-name dev
    create-deployment returns a DeploymentId — remember Components 1.2: nothing above this step was visible to callers until this deployment happened.
  7. Test it and compare against Exercise 1's output.
    curl -s "https://<REST_API_ID>.execute-api.eu-west-1.amazonaws.com/dev/orders/42?expand=items"
    {"order":{"id":"42","status":"CONFIRMED"},"source":"lambda-nonproxy-vtl"} — a differently-shaped, gateway-defined response, versus Exercise 1's function-defined response. Same conceptual endpoint, opposite party (function vs. gateway) controlling the contract.

Exercise 3 — Usage Plan, API Key, and a Live 429

Layer metering and throttling onto the REST API built in Exercise 2, then push past the limit on purpose.

  1. Require an API key on the method, then redeploy.
    aws apigateway update-method \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --resource-id <ID_RESOURCE_ID> --http-method GET \
      --patch-operations op=replace,path=/apiKeyRequired,value=true
    
    aws apigateway create-deployment \
      --region eu-west-1 --rest-api-id <REST_API_ID> \
      --stage-name dev
    apiKeyRequired is now true on the method. A redeploy is mandatory — re-run the earlier curl and confirm it now returns 403 Forbidden with message "Forbidden" (no x-api-key header sent).
  2. Create an API key and a usage plan with a deliberately low throttle so you can hit it in seconds, then link the two together.
    aws apigateway create-api-key \
      --region eu-west-1 \
      --name orders-lab-key --enabled
    
    aws apigateway create-usage-plan \
      --region eu-west-1 \
      --name orders-lab-plan \
      --api-stages apiId=<REST_API_ID>,stage=dev \
      --throttle burstLimit=2,rateLimit=1 \
      --quota limit=1000,period=MONTH
    
    aws apigateway create-usage-plan-key \
      --region eu-west-1 \
      --usage-plan-id <USAGE_PLAN_ID> \
      --key-id <API_KEY_ID> \
      --key-type API_KEY
    create-api-key returns an id and a value (the actual key string). The plan is set to rateLimit=1 request/sec, burstLimit=2 — intentionally tight so throttling is easy to reproduce.
    ⚠️ Why this step matters for the exam

    rateLimit/burstLimit here are the method/stage-level throttle from Components 1.6's token-bucket model — they can only be as generous as the account-level ceiling, never more. This is also the setup for the classic "per-customer quota" exam scenario: a real usage plan would set a realistic rate but the mechanism is identical.

  3. Fetch the key value, then confirm an authenticated call now succeeds.
    aws apigateway get-api-key \
      --region eu-west-1 --api-key <API_KEY_ID> --include-value
    
    curl -s -H "x-api-key: <API_KEY_VALUE>" \
      "https://<REST_API_ID>.execute-api.eu-west-1.amazonaws.com/dev/orders/42?expand=items"
    200 OK with the same VTL-shaped body from Exercise 2 — the key identifies/meters the caller but note it did not need to prove any identity, exactly as Components 1.6 describes.
  4. Trigger and observe the 429.
    for i in $(seq 1 10); do
      curl -s -o /dev/null -w "%{http_code}\n" \
        -H "x-api-key: <API_KEY_VALUE>" \
        "https://<REST_API_ID>.execute-api.eu-west-1.amazonaws.com/dev/orders/42"
    done
    The first couple of lines print 200 (covered by the burst of 2), then subsequent lines print 429 as the sustained request rate exceeds rateLimit=1/sec — this is API Gateway's own throttling response, returned before the Lambda function is ever invoked.

Flashcards — 22 Cards

Click card to flip. Mark right or wrong to track score.

Click to reveal answer
1 / 22
Mark:   Score: 0/0

Practice Quiz — 13 Questions

DVA-C02 scenario style, Easy → Specialty. Select an answer to reveal the explanation.

out of 13 correct