The serverless REST API: API Gateway in front, Lambda in the middle, DynamoDB behind
Three managed services, no servers to patch, and a request path that scales from zero to spike without you touching a load balancer.
The shape of the pattern
This is the reference design that shows up whenever a developer question says 'build a REST API without managing servers.' Three fully managed services do the work: Amazon API Gateway is the front door that terminates HTTPS and exposes the REST resources and methods; AWS Lambda is the compute that runs your handler code; Amazon DynamoDB is the data store that holds the items.
What makes it 'serverless' is not that servers vanish, but that you never provision, patch, or scale them. API Gateway absorbs the incoming requests, Lambda spins up an execution environment per concurrent request, and DynamoDB partitions the data behind a managed key-value engine. You pay per request and per millisecond of compute, and at idle the bill trends toward zero because there is no always-on fleet.
The serverless topology: three managed services, no fleet
Trace
The pattern is three fully managed tiers inside one Region. API Gateway is the front door that terminates HTTPS (with an ACM certificate) and authorizes callers against a Cognito user pool. Lambda is per-request, stateless compute that assumes an IAM execution role. DynamoDB holds the durable state, and CloudWatch collects logs, metrics, and X-Ray traces. Nothing here is an always-on server you provision, patch, or scale by hand, so at idle the bill trends toward zero.
How a request flows
A client sends an HTTPS request with a bearer token to the API Gateway endpoint. API Gateway validates the token against an Amazon Cognito user pool before the request reaches any of your code, so unauthenticated callers are rejected at the edge.
Once authorized, API Gateway uses a Lambda proxy integration: it packages the entire request (path, query string, headers, and body) into a single event object and invokes the function. The handler returns an object containing statusCode, headers, and body, and API Gateway maps that straight back to the caller as the HTTP response. Inside the handler, the code reads and writes DynamoDB using the partition key, and emits logs and traces to CloudWatch and X-Ray. The whole path is synchronous: the client waits while Lambda runs and DynamoDB responds.
How one request flows: proxy integration in and structured response out
Trace
A single synchronous round trip. The client sends HTTPS with a bearer token; API Gateway validates the token against the Cognito user pool before any of your code runs, then uses a Lambda proxy integration to pass the entire request as one event object. The handler assumes its execution role, reads or writes DynamoDB by partition key, and returns a {statusCode, headers, body} object that API Gateway maps straight back to the caller. Logs and X-Ray traces stream to CloudWatch. A malformed body from a proxy handler is what turns into a 502 at the gateway.
Stateless, concurrent, and idempotent
Lambda is stateless. Anything you write to the function's local disk or to a module-level variable may or may not survive to the next invocation, because a request can land on a brand-new execution environment at any time. So each invocation must carry everything it needs, and durable state belongs in DynamoDB, not in the function.
Concurrency scales per request: two hundred simultaneous callers means up to two hundred execution environments running in parallel, each handling exactly one request. That elasticity is why the pattern rides traffic spikes, but it also means a client (or an internal retry) can send the same request twice. The defense is idempotency: use a DynamoDB conditional write (a ConditionExpression) so that a retried 'create order' does not produce a second order, and design handlers so that repeating an operation yields the same result.
Stateless scale-out and idempotent retries
Trace
Concurrency scales per request: N simultaneous callers means up to N execution environments running in parallel, each handling exactly one request, some of them brand-new cold starts. Nothing on local disk or in module-level variables is shared between them, so durable state lives only in DynamoDB. Because a client or an internal retry can send the same 'create order' twice, the write is guarded by a ConditionExpression: the first PutItem with attribute_not_exists(orderId) wins with a 201, and the duplicate is rejected with ConditionalCheckFailedException instead of creating a second order.
What this teaches for the exam
Watch for the proxy-versus-non-proxy distinction: with a Lambda proxy integration the function receives the raw request and must return the statusCode/headers/body shape itself, whereas a non-proxy integration uses mapping templates to transform requests and responses. Questions that mention returning a malformed body from a proxy handler are pointing at a 502 from API Gateway.
At the data layer, remember query versus scan: a Query targets a single partition key and reads only matching items, while a Scan reads every item in the table and is the answer to 'why is this API slow and expensive.' Choose a high-cardinality partition key so access spreads evenly. And when the client should not wait, the synchronous chain is wrong: put a queue or an asynchronous invoke with a dead-letter queue between the layers so failed work is retried and captured instead of lost. Finally, the Lambda execution role, not the caller's credentials, is what grants the function permission to touch DynamoDB, and it should be scoped to least privilege.
When the client should not wait: decouple with a queue and a dead-letter queue
Trace
The exam trap: the synchronous chain is wrong when the client should not wait for slow or failure-prone work. The fix is to put a queue (or an asynchronous invoke) between the layers. The front handler accepts the request, enqueues it, and returns 202 immediately; a worker Lambda drains the queue asynchronously, assumes a least-privilege execution role, and Queries DynamoDB by partition key rather than running an expensive Scan. Messages that keep failing land in a dead-letter queue so the work is captured and retried instead of lost, with CloudWatch alarming on DLQ depth.