1. Situation
A mobile shopping application needs a cart API. Its important operations are predictable:
GET /carts/{userId}
POST /carts/{userId}/items
DELETE /carts/{userId}/items/{productId}
Traffic is quiet overnight and rises sharply during promotions. The team wants to spend its time on application behavior rather than operating web servers, load balancers that distribute requests, database hosts, and scaling groups that add or remove servers as demand changes.
The client expects an immediate answer. When it adds an item, it needs to know whether that request succeeded. This is a synchronous request-response path:
client sends request -> backend performs short work -> client receives response
The data also fits a clear access pattern. Most requests know the userId and need that user's cart. The application does not need arbitrary joins across customers, products, payments, and reports during every cart request.
This makes a serverless API worth considering.
AWS still runs servers. Serverless changes who operates them: AWS provisions, patches, and scales the machines that run the functions, so the application team does not manage that compute fleet.
The team still designs the application. It decides what each API route accepts and returns (the API contract), how the code reads and writes data (the access patterns), and what the code may access through AWS Identity and Access Management (IAM). It must also make retries safe (idempotency), set limits, control cost, and use metrics and logs to understand what the system is doing (observability).
The design question is:
How can the API scale with uneven traffic without replacing server operations with hidden data and retry problems?
2. Naive Design
Naive option 1: keep an Amazon EC2 virtual machine running for a mostly quiet API
Amazon Elastic Compute Cloud (EC2) provides virtual machines in AWS. Running the API on EC2 can be correct, especially for steady workloads or applications that need long-running processes. For a small, uneven API, it creates patching, deployment, capacity, and availability work before the first cart request is handled.
Naive option 2: move every route into one on-demand AWS Lambda function
every API route -> one Lambda function -> every table and secret
AWS Lambda runs function code when it is invoked, while AWS operates the underlying compute. Using Lambda is not the problem here. The problem is that unrelated routes now share code, permissions, deployment risk, and scaling behavior. A bug in an admin route can affect the public cart path, and the function's permissions often become broader than necessary.
Naive option 3: create an Amazon DynamoDB key-value table before defining the reads
Amazon DynamoDB is AWS's managed NoSQL database for data that the application normally finds through known keys. The team instead stores cart items with generated IDs and later asks DynamoDB to find all items for a user through a broad scan. A scan examines many or all table items because the request does not know a key that leads directly to the required cart.
That reverses the useful design order:
unhealthy: create table -> store items -> discover queries later
healthy: list access patterns -> design keys -> store items
DynamoDB is managed, but it cannot invent an efficient key for a query the team never modeled.
3. What Breaks
Follow four failure stories.
Failure 1: the client retries an uncertain write
The client sends POST /carts/user-42/items. The write succeeds, but the response is lost. The client retries and the application adds the same item twice.
The architecture therefore needs idempotency: if the same logical request is delivered more than once, it should still produce one intended result. A stored operation ID or a conditional update, which changes data only when a stated condition is true, can provide that protection.
Failure 2: one key receives disproportionate traffic
In DynamoDB, the partition key is the field used to group and distribute items. A partition key value is the actual value stored in that field—for example, USER#42. Suppose the team instead gives every active cart the same value, such as CART. Requests for many different users would then target one logical group rather than being spread across many user values.
During a promotion, that one value receives a disproportionate number of reads and writes. This is a hot key: one key attracts much more traffic than the others and can become a bottleneck. The architecture needs partition key values that spread traffic according to how the application is really used—in this case, a separate user value for each cart.
Failure 3: automatic scaling overwhelms a dependency
As requests rise, Lambda can run more function invocations at the same time. This is concurrency. If an API called by the function accepts only a small number of connections, increasing Lambda concurrency can overwhelm that API and make it fail faster.
The architecture needs throttling—limiting how quickly new requests are accepted—and concurrency boundaries, not an unlimited number of parallel calls.
Failure 4: long work remains inside the client request
The function calls several slow services, generates a report, and sends emails while the mobile client waits. The request hits a timeout boundary, even though some backend work continues or has already succeeded.
Short request-time work belongs on the synchronous path. Long or independently retryable work should be handed off so the client request can finish while another component continues it. A queue, event route, or workflow can provide that asynchronous boundary.
4. AWS Architecture
Build the API one responsibility at a time.
Step 1: put API Gateway at the front door and define the routes
Amazon API Gateway is the front door to this backend. The client sends an HTTPS request to API Gateway. When authorization is configured, API Gateway first checks whether the caller is allowed to use the API. It then looks at the HTTP method and path—for example, POST /carts/{userId}/items—and sends the request to the connected Lambda function.
In API Gateway terminology, the method-and-path combination is a route. The Lambda function or other backend connected to that route is its integration. API Gateway controls how requests enter and where they go; Lambda runs the application's business logic and may read or update DynamoDB.
client -> HTTPS -> API Gateway -> route + authorization -> Lambda -> DynamoDB if needed -> response
API Gateway offers two products for this request flow: HTTP APIs and REST APIs. Both can expose RESTful HTTP endpoints and invoke Lambda. "REST API" is simply AWS's name for the more feature-rich API Gateway product; a requirement that says "build a RESTful API" does not automatically require that product.
Start with an HTTP API when it provides the routes, Lambda integrations, and authorization the application needs. It is the simpler, lower-cost choice. Choose a REST API when the requirements name one of its additional API-management features:
- API keys and usage plans: identify a consuming application, then apply per-client request-rate limits or quotas, such as 10,000 requests per month. An API key does not authenticate a human user.
- Request validation: reject a request with missing or incorrectly shaped data before it reaches Lambda.
- API Gateway caching: return a stored response for a repeated request instead of invoking the backend every time.
- Request-body transformations: reshape incoming data into the format expected by the backend.
- Private API endpoints: make the API reachable through approved connectivity inside a Virtual Private Cloud (VPC) rather than exposing a public internet endpoint.
- Direct AWS WAF integration: let AWS Web Application Firewall inspect and block unwanted requests at the API boundary.
The exam trap is the product name: RESTful API describes an API style, while API Gateway REST API names a specific AWS product. Choose between HTTP API and REST API from the required features, not from the word "RESTful." Review API Gateway REST API vs HTTP API for the detailed comparison.
Step 2: run short request logic in Lambda
A Lambda function is the code that performs the work after API Gateway selects a route. Connect each route, or a small group of related routes, to a function with one clear responsibility. For example, the cart-writing function can validate an item and update DynamoDB, then return a result while the client waits.
For the normal API Gateway-Lambda path, the client waits for the Lambda response. If the invocation or function fails, API Gateway returns an error; it does not automatically retry that Lambda invocation for the client.
That distinction matters. Client retry policy and write idempotency remain application decisions.
Lambda is appropriate when the work can run inside one function invocation and finish within the request's latency and timeout boundaries. A queue or workflow is a better home for work that should continue independently after the API acknowledges it.
Step 3: design DynamoDB from access patterns
Before creating a DynamoDB table, list the questions the application must answer. For this cart API, those questions include "get this user's cart" and "update this product in that cart." These required reads and writes are the application's access patterns.
DynamoDB works best when each request already knows values that lead directly to the required data. Here, the userId can locate one cart, and the productId can locate one item inside it. Those values become the basis for the partition key and optional sort key. Operations such as GetItem, UpdateItem, and Query can then use known keys instead of searching the whole table with a scan.
| Access pattern | Value the request knows | Efficient key idea |
|---|---|---|
| Get a user's cart | userId | Partition by user |
| Get one product in the cart | userId and productId | User partition plus product sort key |
| Add or change an item | userId and productId | Update the exact keyed item |
DynamoDB examples often abbreviate partition key as PK and sort key as SK. A simple item might be:
PK = USER#42
SK = ITEM#product-9
quantity = 2
The partition key determines which logical group an item belongs to and helps DynamoDB distribute that data across its underlying storage. Here, USER#42 puts one user's cart items together. The optional sort key distinguishes items inside that group, so ITEM#product-9 identifies one product while a query for the USER#42 partition can return the whole cart.
A Global Secondary Index (GSI) gives DynamoDB another key-based way to find the same data. For example, if the table is keyed by userId but the application must also find a cart by checkoutSessionId, a GSI can support that second lookup. DynamoDB maintains the extra index whenever data changes, so a GSI adds storage, write work, cost, and another key design to get right.
Choose Amazon Relational Database Service (RDS) or Amazon Aurora—AWS services for relational databases—instead when relational joins, flexible SQL, and relational transactions are central requirements rather than exceptions.
Step 4: separate client identity from function identity
There are two different trust questions:
May this client call the API?
May this Lambda function call DynamoDB?
API Gateway authorization answers the first question before the request reaches the application code. The configured authorizer checks the caller's proof of identity or permission. IAM authorization checks a request signed with AWS credentials. For an HTTP API, a JSON Web Token (JWT) authorizer checks a signed token issued after a user logs in; Amazon Cognito can manage those users and issue the tokens. For a REST API, the comparable built-in choice is a Cognito user pool authorizer. A Lambda authorizer runs custom authorization code when the built-in choices do not fit.
The Lambda execution role is the function's AWS identity and answers the second question. While the function runs, Lambda automatically provides short-lived credentials for that role. Grant the cart function only the DynamoDB operations it needs, such as reading or updating items, and only on the required table. API authorization does not automatically give the function database permission, and the execution role does not authenticate the mobile user.
Step 5: make repeated writes safe
Give a create-style request an idempotency key, such as an operation ID generated once by the client. The client reuses that same ID if it retries the request. Store or enforce the ID with a DynamoDB conditional write, which succeeds only when a stated condition is true—for example, only when that operation ID has not already been recorded.
DynamoDB checks the condition and performs the write as one atomic operation. That means two concurrent requests cannot both win the same "not recorded yet" check.
first request with operation-abc -> condition passes -> write succeeds
retry with operation-abc -> already recorded -> return prior outcome
A lost update happens when one writer unknowingly overwrites a newer value. Conditional writes can prevent this by changing an item only when its version or current value still matches what the application expects.
Idempotency does not mean "ignore every duplicate." It means repeated delivery of the same logical request produces one intended business effect.
Step 6: protect each capacity boundary
Managed services can scale quickly, but "scales automatically" does not mean "accepts unlimited work." This request crosses three different capacity boundaries: how quickly requests enter, how many Lambda invocations run at once, and how much DynamoDB read/write work is available. Each needs its own control:
- API Gateway throttling limits the rate at which requests enter. It can reject excess requests before all of them become Lambda work.
- Lambda concurrency is the number of function invocations running at the same time. Functions in one AWS account and Region—a geographic AWS deployment area—share concurrency capacity. Reserved concurrency both guarantees part of that capacity for one function and caps how many of its invocations may run at once, which can protect a fragile dependency.
- DynamoDB on-demand capacity lets AWS adjust the table's read and write capacity automatically, so the team does not choose a fixed throughput in advance.
- With provisioned capacity, the team specifies expected read and write throughput. Auto scaling can adjust it around a target, which can suit predictable traffic that is worth tuning.
These controls do not replace good keys. A bad hot key remains a concentrated access pattern even when the table uses on-demand capacity.
Step 7: add recovery and observability
Enable DynamoDB point-in-time recovery (PITR) when accidental writes or deletes require historical restoration. PITR keeps continuous backups so the table can be restored to an earlier point in its enabled recovery window. It creates a new table; it does not silently rewind the production table in place.
Use Amazon CloudWatch—AWS's service for metrics, logs, and alarms—to observe the full request path:
- API Gateway latency, integration latency (time waiting on the backend),
4XX, and5XXresponses; - Lambda duration, errors, throttles, and concurrency;
- DynamoDB throttling, consumed capacity (the read and write capacity used), and system errors; and
- application-level outcomes such as successful cart updates.
The completed architecture is:
5. Request Or Data Flow
Learn three flows: a read, a retry-safe write, and work that leaves the request path.
Flow 1: read the cart
- The client sends
GET /carts/user-42with its authentication token. - API Gateway authorizes the client and matches the route.
- API Gateway invokes the cart-reading Lambda function synchronously.
- Lambda uses its execution role to query the
USER#42partition. - DynamoDB returns the matching items.
- Lambda formats the response, and API Gateway returns it to the client.
Flow 2: retry-safe cart update
If the response is lost and the client repeats the same operation ID, the function can recognize the same logical request rather than applying a second business effect.
Flow 3: accept long-running work
Suppose the user asks the API to generate a large cart export. Do not keep the HTTP request open while Lambda performs every step.
- Validate and authorize the request.
- Create a job record.
- Hand off the work to a managed service that retains or tracks it after the API responds: use Amazon Simple Queue Service (SQS) for a buffered backlog of jobs waiting for workers, EventBridge to route an event to interested targets, or Step Functions to coordinate an ordered workflow.
- Return HTTP
202 Acceptedwith a job identifier. This means the work was accepted for background processing, not that it has finished. - Let the client check status or receive a later notification.
The next scenario develops those asynchronous choices.
6. Security Controls
Authenticate the client at the API boundary
Choose an authorization mechanism that fits the caller. Do not treat an API key as user authentication. In API Gateway REST APIs, an API key mainly identifies a caller for usage tracking and for a usage plan, which applies configured quotas and throttling; it does not prove who the user is.
Give each function a narrow execution role
GetItem and Query are DynamoDB read operations, so a cart reader may need them. A cart writer may need selected write operations. Neither automatically needs permission to delete the table, read unrelated secrets, or access every DynamoDB table.
Validate at the application boundary
Authorization answers whether the caller may use the operation. Validation still checks path parameters, payload shape, quantities, allowed fields, and business rules. DynamoDB's flexible attributes do not make malformed input safe.
Protect sensitive data and logs
DynamoDB encrypts stored data. AWS Key Management Service (KMS) manages the encryption keys. A customer-managed KMS key is a key that your AWS account creates and controls; choose one when the requirement calls for controlling who may use the key through its key policy, when the underlying encryption key is rotated, or how its use is audited.
Do not log authentication tokens, secrets, or full sensitive request bodies. Set a deliberate retention period—the length of time CloudWatch keeps those logs—instead of storing them indefinitely by accident.
7. Resilience Controls
API Gateway, Lambda, and DynamoDB are managed regional services: AWS operates their underlying infrastructure within a Region. That removes server operations, but application correctness still determines whether the API is resilient.
Use a maximum retry count and backoff—waiting longer between attempts—for temporary failures. Retry reads more freely than uncertain writes. Require idempotency for create-style operations that might be repeated.
Use conditional writes for conflict-sensitive updates. A condition can prevent one client from overwriting a value that changed after it was read.
Enable DynamoDB PITR for important tables and practice restoring into a new table. Recovery is incomplete until the application can validate and deliberately switch to restored data.
Use reserved concurrency when one function must not consume all shared Lambda concurrency or when a downstream dependency needs a ceiling. For example, a limit of 50 prevents this function from running more than 50 invocations at once. That ceiling can protect a database or partner API, but additional requests may then be throttled, so monitor both the function and the dependency.
For work that should survive and retry independently after the API responds, send a message to a durable queue such as SQS. The normal synchronous API Gateway-to-Lambda request does not store unfinished work like a queue.
8. Performance Controls
Measure latency by layer:
client-visible latency
= API Gateway work
+ Lambda initialization and execution
+ DynamoDB calls
+ network and serialization time
When Lambda starts a function, it creates an isolated runtime and loads the code. AWS calls this an execution environment, and Lambda may reuse it for later requests. Create reusable AWS Software Development Kit (SDK) clients outside the handler—the function's entry point—so a reused environment can keep their connections and configuration instead of rebuilding them for every invocation.
Provisioned concurrency keeps a chosen number of Lambda execution environments initialized and ready. It can reduce startup latency for paths with strict, predictable latency needs, but it adds cost. Use it for a measured requirement, not as a default for every function.
Design DynamoDB keys to spread real traffic. A table-wide average can look healthy while one key receives disproportionate requests.
Use Global Secondary Indexes (GSIs) only for real alternate key-based queries. Add a cache only when measurements show that many requests repeatedly read the same data. DynamoDB Accelerator (DAX) is one AWS option: it keeps frequently requested DynamoDB results in memory so repeated reads can return faster, but it also adds another component and cost.
9. Cost Controls
Instead of paying for one server, the bill follows several separate usage categories:
- API Gateway request volume and API type;
- Lambda invocation count, duration, and configured memory;
- DynamoDB reads, writes, storage, indexes, backups, and capacity mode;
- CloudWatch logs, metrics, and retention;
- KMS requests, data transfer, and optional features.
HTTP APIs are often the cost-efficient choice when their features are sufficient. REST APIs are justified when their additional management capabilities satisfy a real requirement.
Choose DynamoDB on-demand for unknown or uneven traffic when low capacity-management effort matters. Consider provisioned capacity with auto scaling for predictable steady workloads where tuning is worth the effort.
Lambda memory also changes available CPU. More memory can sometimes finish work fast enough to lower total duration cost, so measure rather than assuming the smallest setting is cheapest.
Avoid verbose permanent logs for every successful request. Retain the information needed to investigate and operate the API, not an accidental second copy of every payload.
10. Exam Variants
| If the question says... | Think... | Why |
|---|---|---|
| "Public API with low operational overhead" | API Gateway + Lambda | Managed API entry and request-time compute avoid an application-server fleet |
| "Simple lower-cost Lambda HTTP API" | API Gateway HTTP API | Use it when its feature set covers the requirement |
| "API keys, usage plans, request validation, caching, or private API" | API Gateway REST API | Those are REST API decision signals |
| "Fully managed NoSQL with known key access" | DynamoDB | It stores items addressed through known keys without database hosts |
| "Unpredictable DynamoDB traffic" | On-demand capacity | It reduces read/write capacity planning |
| "Predictable steady DynamoDB traffic and cost tuning" | Provisioned capacity with auto scaling | Capacity can be tuned to the known workload |
| "Prevent duplicate write after retry" | Idempotency key + conditional write | The same logical request should create one business effect |
| "Protect a fragile downstream dependency" | API throttling and/or Lambda concurrency cap | Control how much parallel work reaches it |
| "Recover a table after accidental deletion" | DynamoDB PITR | Restore historical table state into a new table |
| "Relational joins and flexible SQL are central" | RDS or Aurora | DynamoDB should not be forced into a relational access model |
| "Long background work after API acknowledgement" | SQS, EventBridge, or Step Functions | Move independently retryable work off the synchronous path |
11. Common Traps
| Trap | Better reasoning |
|---|---|
| "Serverless means there are no servers or limits." | AWS operates the fleet; you still design around quotas, concurrency, timeouts, and downstream capacity. |
| "RESTful API means API Gateway REST API." | Choose HTTP API or REST API from required product features, not the API style's name. |
| "API Gateway retries a failed Lambda request for me." | The normal synchronous integration returns the error; client retry safety remains your responsibility. |
| "API keys authenticate users." | API keys support client identification and usage-plan behavior; use an authorization mechanism for identity. |
| "Lambda's execution role authenticates the caller." | The authorizer controls client access; the execution role controls the function's AWS permissions. |
| "DynamoDB is schema-free—items may have different non-key attributes—so keys can be designed later." | Attribute flexibility does not remove the need to model access patterns and partition or sort keys first. |
| "On-demand capacity fixes a hot key." | Capacity mode does not make a concentrated access pattern well distributed. |
| "Every Lambda error is safe to retry." | A write may have completed before the response failed; tie idempotency to the request's operation ID. |
| "PITR rewinds the production table." | DynamoDB restores to a new table that must be validated and adopted deliberately. |
| "Put long work in the API Lambda because it scales." | Request-time limits and client latency still apply; move independent work to an asynchronous boundary. |
Final Mental Model: One-Minute Review
| Term | Exact job | Remember it as |
|---|---|---|
| API Gateway | Publishes the HTTPS API, matches routes, authorizes clients, and controls incoming requests | The API reception desk |
| Lambda | Runs short-lived application logic for each request | An on-demand worker |
| Lambda execution role | Gives the function temporary permission to call AWS services | The worker's job badge |
| DynamoDB | Stores items for known key-based access patterns | A distributed keyed filing cabinet |
| Partition key | Distributes and locates related data | The cabinet section label |
| Conditional write and idempotency key | Prevent repeated or conflicting writes from creating unintended effects | A one-operation claim ticket |
| Throttling and concurrency | Limit how much work enters or runs in parallel | The admission and staffing limits |
| PITR | Restores earlier table state into a new table | The table time machine |
The synchronous path is API Gateway → Lambda → DynamoDB. The authorizer decides whether the client may enter; the execution role decides what Lambda may call; the key design decides whether DynamoDB can answer efficiently; idempotency makes retries safe.
12. Related Topics
Use Amazon API Gateway for routes, stages, endpoint types, authorization, and integrations. Review AWS Lambda for execution environments, concurrency, permissions, and invocation models. Use Amazon DynamoDB for deeper key, index, capacity, stream, and global-table design.
Continue to Event-Driven Order Processing when work must be routed, buffered, retried, or coordinated after the API returns.
Official AWS references: