skip to content

API Gateway

API Gateway is the managed front door for your APIs: REST, HTTP, and WebSocket flavours, routes and stages, authorizers, throttling, and Lambda integration. It shows up in serverless designs, where the questions are usually about authorization and rate limiting rather than routing.

part ofAWSoverview, primer and where to startread it →
on this pageshow

explore

questions

16

An API Gateway route uses the Lambda proxy (`AWS_PROXY`) integration and every call returns HTTP 502 with `{"message": "Internal server error"}`, yet the function's own CloudWatch logs show it completing without error. What is wrong, and what must a Lambda proxy handler return?

level: juniorimportance: must knowfreq 60%

answer

  1. proxy means no transformation layer
  2. the return value IS the response
  3. check the shape, not the logic
  4. body must already be a string
  5. statusCode number, body string, isBase64Encoded

basics

~20 s

The function returned a shape API Gateway cannot map to an HTTP response. A Lambda proxy handler must return an object with a numeric statusCode, an optional headers map, and a body that is already a string — returning a raw object or a bare value produces 502.

solid answer

~50 s

With the `AWS_PROXY` integration, API Gateway does no transformation: whatever the function returns *is* the HTTP response, so it must match a fixed shape — `statusCode` as a number, optional `headers` (and `multiValueHeaders` on REST APIs), `body` as a **string**, and `isBase64Encoded` for binary payloads. If the function returns a plain object, forgets `statusCode`, or leaves `body` as an object instead of `JSON.stringify`-ing it, API Gateway cannot build a response and answers 502 with the generic `Internal server error` message — which is why the function's logs look clean. The fix is to stringify the body and return the envelope. One wrinkle: on HTTP APIs using payload format 2.0, a function that returns valid JSON with no `statusCode` is treated as a 200 with that JSON as the body, so the same handler can "work" on an HTTP API and 502 on a REST API.

code

javascript · 17 lines
javascript
// Correct AWS_PROXY response envelope (payload format 1.0)
export const handler = async (event) => {
  try {
    const id = event.pathParameters?.id;
    if (!id) {
      return { statusCode: 400, body: JSON.stringify({ error: "missing id" }) };
    }
    return {
      statusCode: 200,
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ id })   // string, not an object
    };
  } catch (err) {
    console.error(err);
    return { statusCode: 500, body: JSON.stringify({ error: "internal" }) };
  }
};

go deeper

for a junior

Memorise the envelope: numeric statusCode, optional headers, and a body you have already stringified. Say plainly that with proxy integration your return value is the HTTP response.

for a middle

Explain that no transformation layer exists in AWS_PROXY, and separate the failure modes: 502 for malformed or thrown, 504 for integration timeout, 500 with AccessDeniedException for invoke permission.

for a senior

Demonstrate the diagnosis path — execution logs or $context.integrationErrorMessage in the access log — and show the codified error envelope you would put in a shared handler wrapper so callers never see an opaque 502.

for a principal

Own the platform contract: one shared response/error helper across services, a house standard on payload format, and log fields chosen so an operator can tell gateway faults from backend faults without reading code.

## What proxy integration actually means API Gateway integrations come in two families. A **non-proxy** (custom) integration lets you write mapping templates that translate the HTTP request into whatever the backend wants, and translate the backend's answer back into an HTTP response. A **proxy** integration deliberately removes that layer: the whole request is handed to the backend as a structured event, and the backend's return value is taken as the whole response. For Lambda that integration type is `AWS_PROXY`. That trade is the source of this failure. Because there is no mapping template to fix things up, the contract on the return value is exact, and any deviation is unmappable. ## The required response shape For the REST-era payload format (format 1.0), the handler must return an object with: - `statusCode` — a **number**, required. - `headers` — optional object of single-valued headers. - `multiValueHeaders` — optional object whose values are arrays, for headers that repeat (`Set-Cookie`). - `body` — a **string**. Not an object, not a number. - `isBase64Encoded` — boolean; when `true`, `body` is base64 and API Gateway decodes it into binary bytes (the media type must also be listed in the API's binary media types). ```javascript export const handler = async (event) => ({ statusCode: 200, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: event.pathParameters.id }) }); ``` Return `{ statusCode: 200, body: { id: 1 } }` and you get 502, because `body` is an object. Return `{ id: 1 }` and you get 502, because there is no `statusCode`. ## Why the logs look innocent The function *succeeded*. It ran, logged, and returned. The failure happens afterwards, inside API Gateway, when it tries to turn that return value into an HTTP response. That is exactly why the generic `Internal server error` body is so unhelpful and why the debugging move is to look at the **API Gateway execution logs** (or the access log with `$context.integrationErrorMessage`), not at the function logs. Execution logs will say the response was malformed. ## The 502 / 504 / 500 triage Worth internalising, because interviewers push on it: - **502 Bad Gateway** — the function threw an unhandled error, or returned a shape API Gateway cannot map. - **504 Gateway Timeout** — the integration exceeded API Gateway's limit (tens of seconds), even if the function itself keeps running to its own longer timeout. Raising the Lambda timeout does not help. - **403** — usually authorization or a resource-policy denial, before the integration is reached at all. - **500 with `AccessDeniedException` in the execution log** — API Gateway is not permitted to invoke the target function. ## Payload format 1.0 versus 2.0 REST APIs always use format 1.0. HTTP APIs default to **2.0** and let you pin 1.0. Two differences bite people: 1. The **event** is flatter in 2.0: `event.requestContext.http.method` rather than `event.httpMethod`, `event.rawPath`, `event.rawQueryString`, `cookies` as an array rather than folded into headers. A handler written for a REST API often breaks when copied to an HTTP API. 2. The **response** is more forgiving in 2.0: if the function returns valid JSON without a `statusCode`, API Gateway infers `200` and uses the JSON as the body with `application/json`. Convenient, but it means the strict envelope is a habit you should keep, because the identical handler behind a REST API 502s. ## When to use non-proxy instead Proxy integration puts the HTTP contract in your code, which is usually what you want: the handler is testable, the API definition is thin, and there is no VTL to maintain. Non-proxy integration earns its keep when the backend cannot be changed to speak the envelope — a legacy service, or an `AWS` integration that calls another service directly with no Lambda at all. The price is mapping templates written in Velocity Template Language, plus integration responses that select an HTTP status by regex-matching the error string the backend returned. Teams routinely underestimate how unpleasant that is to own and how hard it is to unit-test, which is why proxy integration is the default recommendation. ## The habit that prevents the bug Wrap every handler's exit in a small helper that builds the envelope, and never return a raw value from business logic. Then a thrown error becomes a deliberate 4xx/5xx envelope with a useful body, instead of an unhandled exception that surfaces to the caller as an opaque 502.

  • How do you get a useful error message instead of the generic `Internal server error`?
    Turn on API Gateway execution logging for the stage, or add `$context.integrationErrorMessage` and `$context.error.message` to the access log format. Those tell you whether the response was malformed, the invocation failed, or permission was denied. Then handle errors inside the function so the caller receives a deliberate status and body rather than an unhandled exception.
  • The same handler returns 502 behind a REST API but 200 behind an HTTP API. Why?
    HTTP APIs default to payload format 2.0, where a function returning valid JSON without a `statusCode` is interpreted as a 200 with that JSON as the body. REST APIs use format 1.0 and require the full envelope, so the missing `statusCode` is unmappable and becomes 502. Always return the explicit envelope so behaviour does not depend on the API type.
  • When would you deliberately use a non-proxy Lambda integration instead?
    When the backend's contract cannot change — a legacy function or another AWS service you are calling directly with no Lambda at all — and you need mapping templates to reshape request and response. You pay for it in VTL you must maintain and integration responses that pick a status code by regex-matching the error string, which is much harder to test than code.

saying these in an interview costs you the question

  • Blames the function's business logic when the shape is wrong
  • Returns the body as an object instead of a string
  • Thinks a 502 always means the backend crashed
  • Assumes raising the Lambda timeout cures a 504
  • Believes proxy integration still applies mapping templates

context

open as a page

Amazon API Gateway offers REST APIs and HTTP APIs (as well as WebSocket APIs). What would make you choose a REST API over an HTTP API, and what are you giving up if you default to HTTP APIs?

level: middleimportance: must knowfreq 70%

basics

~20 s

HTTP APIs are cheaper and lower-latency, so default to them for plain Lambda or HTTP backends. Choose REST APIs when you need what HTTP APIs omit: API keys and usage plans, stage response caching, request validation, VTL mapping templates, private endpoints, or AWS WAF.

open as a page

You are choosing how Amazon API Gateway will authorize callers of a new API. Compare IAM (SigV4) authorization, a Cognito user pool authorizer, an HTTP API JWT authorizer, and a Lambda authorizer — what decides which one you pick?

level: middleimportance: must knowfreq 66%

basics

~20 s

Pick by who the caller is. AWS principals get IAM SigV4. End users with tokens from an OIDC issuer get the built-in JWT authorizer on an HTTP API, or a Cognito user pool authorizer on a REST API. Anything else needs a Lambda authorizer.

open as a page

A REST API in Amazon API Gateway is protected by a Lambda authorizer. What exactly must that function return for the request to be allowed, and how does the backend integration learn who the caller was?

level: middleimportance: must knowfreq 70%

basics

~20 s

A REST API Lambda authorizer must return a principalId, plus a policyDocument — an IAM policy allowing execute-api:Invoke on the requested method ARN. Anything in the optional context map is passed through to the integration as caller identity.

open as a page

Amazon API Gateway lets you set throttling limits at the account, stage, method or route, and usage-plan levels. Explain what the rate and burst values in a throttle pair actually control, and which of those layers decides whether a given request is rejected.

level: middleimportance: must knowfreq 60%

basics

~20 s

Each API Gateway throttle is a token bucket: burst is the bucket's capacity, rate is how many tokens per second refill it. A request is rejected by the first empty bucket it meets, checked from the most specific usage-plan limit outward to the region-wide account limit.

open as a page

You are launching a public REST API on Amazon API Gateway with a free tier and a paid tier. How do usage plans give each customer its own rate limit and monthly allowance, and what are the practical limits of that mechanism?

level: middleimportance: must knowfreq 52%

basics

~20 s

An API Gateway usage plan binds a rate/burst throttle and a request quota per day, week or month to API keys, and associates them with specific API stages. Each customer gets its own key, so tiers are just plans with different numbers. Usage plans exist for REST APIs only.

open as a page

In Amazon API Gateway, does requiring an API key (the `x-api-key` header) authenticate the caller? What does an API key actually establish, and what would you add if you need real access control?

level: juniorimportance: should knowfreq 52%

basics

~20 s

No. An API Gateway API key only identifies a client for metering and rate limiting — it is a static string in a header, not a credential. Real access control needs IAM SigV4, a Cognito or JWT authorizer, or a Lambda authorizer.

open as a page

A client calling an Amazon API Gateway REST API starts receiving HTTP 429 responses. Which two different API Gateway conditions produce a 429, how do you tell them apart, and how should the client react to each?

level: juniorimportance: should knowfreq 68%

basics

~20 s

API Gateway returns 429 for two reasons: a rate or burst throttle was hit, or a usage-plan quota is exhausted. A throttle clears within seconds, so retry with exponential backoff and jitter; a quota only resets at its day, week or month boundary.

open as a page

With an Amazon API Gateway WebSocket API, how does your backend send a message to a client after that client has connected, and what must happen on the `$connect` route for it to be possible?

level: middleimportance: should knowfreq 40%

basics

~20 s

API Gateway gives every connection a connectionId, which your $connect handler must persist (typically in DynamoDB) alongside the user it belongs to. The backend then pushes by calling PostToConnection on the API's @connections management endpoint, needing the execute-api:ManageConnections permission.

open as a page

In Amazon API Gateway, what is the difference between a private API and a private integration, and what mechanism implements each one?

level: seniorimportance: should knowfreq 45%

basics

~20 s

They control opposite directions. A private API restricts who may call the API: it is a REST API with the PRIVATE endpoint type, reachable only through an interface VPC endpoint for execute-api. A private integration is how the API reaches a backend inside a VPC, using a VPC link.

open as a page

In an API Gateway REST API, what are stages and stage variables, and what commonly breaks when a stage variable selects which Lambda alias the stage invokes?

level: seniorimportance: should knowfreq 33%

basics

~20 s

A stage is a named, invocable snapshot of a deployment (dev, prod), and stage variables are per-stage key/value pairs referenced as ${stageVariables.name} inside integration URIs and mapping templates. When a variable picks the Lambda alias, API Gateway cannot add the invoke permission for you, so calls fail with 500 until you add it per alias.

open as a page

A REST API in Amazon API Gateway enables authorizer result caching (`authorizerResultTtlInSeconds`) on its Lambda authorizer. What is cached and what is the cache key, and what goes wrong in production because of it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

API Gateway caches the authorizer's whole response — policy and context — keyed by the token for a TOKEN authorizer or the combined identity sources for a REQUEST authorizer. That creates a revocation lag equal to the TTL, and reuses a narrow policy on other methods.

open as a page

A public API on Amazon API Gateway can be protected by stage throttles, per-key usage-plan quotas, and an AWS WAF web ACL with a rate-based rule associated with the stage. How would you decide which of these layers to use so that one abusive caller cannot degrade everyone else?

level: principalimportance: should knowfreq 35%

basics

~20 s

Each layer limits a different thing: stage throttles cap total load but cannot tell callers apart, usage-plan quotas cap a known customer's volume, and a WAF rate-based rule caps an anonymous source by IP or another aggregation key. Anonymous abuse needs WAF; identified abuse needs usage plans.

open as a page

You want an API Gateway API served at https://api.example.com instead of its default execute-api URL. What do you configure, and why does the endpoint type decide which region the ACM certificate must live in?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

Create an API Gateway custom domain name with an ACM certificate, add a base-path/API mapping to a specific API and stage, then point DNS at the target domain the service returns. An edge-optimized domain is fronted by CloudFront, so its certificate must be in us-east-1; a regional domain needs the certificate in the API's own region.

open as a page

A team in a different AWS account must call your Amazon API Gateway REST API that uses `AWS_IAM` authorization. What has to be in place on both sides, and what can the API's resource policy express that an identity policy cannot?

level: seniorimportance: nice to knowfreq 36%

basics

~20 s

Cross-account access needs both sides to allow it: the caller's IAM identity policy must permit execute-api:Invoke on your method ARN, and your API's resource policy must allow their principal. The resource policy adds network conditions such as source IP, VPC endpoint or organization ID.

open as a page

An Amazon API Gateway REST API stage has response caching enabled with a 300-second TTL, but two problems appear: the cache hit rate is near zero, and occasionally one customer receives another customer's response. What determines what gets cached and returned, and how would you fix both symptoms?

level: seniorimportance: nice to knowfreq 38%

basics

~20 s

API Gateway stage caching keys entries on the method request parameters you explicitly designate as cache key parameters. Omit the parameter that varies and every caller shares one entry — the cross-customer leak. Include a parameter that is unique per request and nothing ever hits.

open as a page