Serverless API
Serverless API
Architecture
- Client — Web or mobile client. Signs in against Cognito, then calls the API with a Bearer JWT.
- Cognito User Pool — Hosted sign-up/sign-in. Issues ID + access JWTs; API Gateway validates them via a Cognito authorizer on every route.
- API Gateway (REST) — Single REST entry point. Cognito authorizer on all routes, request validation from the OpenAPI contract, throttling, and Lambda proxy integration.
- Tasks Handler — Node.js Lambda (proxy integration) implementing all /tasks routes. Reads the caller's userId from the JWT claims — every query is scoped to the caller.
- Tasks Table — Single-table design. PK USER#<userId>, SK TASK#<taskId>; GSI1 supports list-by-status ordered by due date. On-demand capacity.
- CloudWatch — Structured JSON logs, Lambda metrics + p99 latency alarms, and API Gateway access logs.
About this design
- # About this design ## What this is The canonical serverless REST API: **API Gateway + Lambda + DynamoDB**, with **Cognito** for auth and **CloudWatch** for observability. The example domain is a per-user task manager (CRUD on `/tasks`), but the shape is domain-agnostic — swap "task" for any resource your product needs. Everything here scales to zero, has no servers to patch, and costs nothing when idle. ## How it works 1. **Auth happens before your code runs.** Clients sign in against the Cognito user pool and send a Bearer JWT. A Cognito authorizer on API Gateway validates the token against the pool's JWKS; invalid or expired tokens are rejected with `401` at the gateway — the Lambda is never invoked (see the sequence diagram). 2. **One Lambda, proxy integration.** The Tasks Handler implements every `/tasks` route. It reads the caller's identity from `event.requestContext.authorizer.claims.sub` and scopes **every** read and write to that user — there is no way to address another user's data, by construction. 3. **Single-table DynamoDB.** Items live under `PK = USER#<userId>`, `SK = TASK#<taskId>`. Listing a user's tasks is one Query; fetching one task is a GetItem. `GSI1` (`USER#<id>#STATUS#<status>` → due date) answers "my open tasks by due date" without scans. Writes use condition expressions so updates and deletes only succeed against items the caller owns. 4. **Request validation at the edge.** API Gateway validates request bodies against the models in the API contract, so malformed input returns `400` without a cold start. 5. **Observability.** The handler logs structured JSON to CloudWatch; API Gateway access logs and Lambda error/latency alarms complete the picture. ## How to extend - **Add a resource:** define its routes in the API contract, add an item type to the table (e.g. `SK = PROJECT#<id>`), and either extend the handler's router or add a second Lambda per resource — the authorizer covers new routes automatically. - **Add background work:** enable DynamoDB Streams on the table and attach a consumer Lambda (e.g. send a reminder when `due_date` approaches), or publish events to EventBridge from the handler. - **Add roles:** put users in Cognito groups (`admin`, `member`); the group claim arrives in the JWT and can gate routes in the handler. - **Harden for production:** add WAF in front of the gateway, per-route throttling, X-Ray tracing, and a DLQ on any async consumers. Deploy with CDK, SAM, or Terraform — the design maps one-to-one onto any of them.