Amazon API Gateway

A fully managed REST API service: API types, caching, backend targets beyond Lambda, and authorizing directly against Cognito without a custom Lambda.

August 29, 20265 min read2 / 15

A fully managed RESTful API service. Web apps, mobile apps, IoT devices, and private (VPC or on-premises) clients all reach it over standard HTTP, GET, POST, PUT, DELETE. API Gateway logs request data to CloudWatch automatically.

API types

  • REST API: the original, full-featured type.
  • HTTP API: a newer, lower-latency, lower-cost type. Fewer features than REST API, but cheaper and faster, worth defaulting to unless a specific REST API feature is actually needed.
  • WebSocket API: for real-time, bidirectional communication. AWS AppSync is the alternative worth knowing about specifically for a real-time GraphQL API.
  • Private API: not exposed to the public internet, reachable only from inside a VPC.

Endpoint types (for REST APIs) add another axis: regional (served from one AWS region), edge-optimized (served through CloudFront's edge network, worth it when users are spread globally), and private. Reaching a resource inside a VPC from API Gateway goes through a VPC link, a private connection into that network.

Caching

API Gateway can cache responses itself. On a GET request, it checks its own cache before calling the backend at all; a cache hit skips the backend (Lambda, DynamoDB, whatever) entirely and returns straight from the cache, which is a real latency win on read-heavy endpoints. A cache miss calls the backend as normal and populates the cache with the result for next time.

Backend targets beyond Lambda

Lambda behind API Gateway, both serverless, is the default pairing (often called the traditional serverless web architecture), but it's not the only option. API Gateway can also call other AWS services directly, or any publicly accessible HTTP endpoint, including something outside AWS entirely, without a Lambda function in the middle.

The non-obvious benefit of routing an already-public endpoint through API Gateway anyway: the same authorization logic configured at the gateway applies to that call too. A public endpoint reached through API Gateway still has to pass whatever authorizer is attached, even though the endpoint itself doesn't enforce that on its own.

Cognito as a direct authorizer

An API Gateway route can point straight at a Cognito user pool as its authorizer, no custom authorizer Lambda needed for a plain token check. Every request carries a token; API Gateway validates it against the pool before the request goes anywhere else. A missing, expired, or invalid token gets rejected with a 401 before any application code runs.

The payoff: the check is enforced once, at the gateway, instead of reimplemented inside every Lambda function that needs it.

Wiring a Lambda behind a route

The minimum needed to expose one Lambda function as an HTTP endpoint: an HTTP method (GET, POST, and so on) and a path (/hello, /notes). API Gateway creates a resource for that path, attaches the method to it, and integrates the method with the Lambda function, so a matching request invokes it and returns whatever the function returns.

Every deployment lands on a stage, a named environment (dev by default) that becomes part of the URL: https://{api-id}.execute-api.{region}.amazonaws.com/{stage}/{path}. The region in that URL is whichever region the API itself was deployed to, worth checking directly if a request isn't reaching the function, a mismatched region is a common first mistake.

Both the created API and the Lambda function it invokes are visible in their own console pages, API Gateway's console shows the method and path tree; Lambda's console shows the function with API Gateway listed as a trigger.

Lambda proxy integration, why the whole request just arrives

Proxy integration means API Gateway does no translation in either direction. Every part of the incoming request, path parameters, query strings, headers, the body, gets bundled into one event object and handed to the Lambda exactly as it arrived. Whatever the Lambda returns goes back to the client exactly as returned, no reshaping at the gateway either way.

That's the entire reason event.pathParameters, and the rest of the event's shape, exists at all: the gateway isn't picking out individual fields to forward, it's forwarding the whole request as one object and letting the Lambda decide what to read from it. The alternative, a non-proxy integration, lets API Gateway itself transform the request and response through mapping templates, more setup, more control over what a Lambda ever sees, and mostly unnecessary next to just reading what you need off the proxy event.

event.body arrives as a raw string, not a parsed object, exactly like the rest of the request, untouched. A POST with a JSON payload needs JSON.parse(event.body) before any of its fields are usable, forgetting that parse step is a common first bug, the fields don't come back undefined, the whole data variable is just a string.

In the console, this shows up as the method's integration request (client → Lambda) and integration response (Lambda → client) both being marked as proxy, one setting, applied both ways.

Dynamic path parameters

A path can include a variable segment, /notes/{id}, so one route handles every note instead of one route per ID. Inside the Lambda's handler, that value arrives on the event object the Lambda proxy integration passes in, under event.pathParameters, keyed by the same name used in the path definition ({id} in the path means event.pathParameters.id in the handler).

The name has to match exactly on both sides. A path defined as {noteId} won't populate event.pathParameters.id, only event.pathParameters.noteId, a common source of an undefined value that looks like a missing request field but is actually a naming mismatch between the route and the handler.

Direct service integrations

API Gateway can call some AWS services directly, no Lambda required as a transport layer. Used here to publish an event straight to EventBridge from a registration request. Skip the Lambda unless the request actually needs to be transformed before it reaches the target service.

Where this showed up

Further Reading and Watching