Module 10: Capstone Project
Why this matters
Every module so far handed you scaffolding — a code block to paste, an
expected output to check against, a hint when you got stuck. This one doesn't.
The capstone is the integration test for the whole track, and the only way to
find out whether the knowledge actually transferred is to build something
substantial from an empty folder with no solution to peek at. The guided
exercises built recognition: you could follow along and it made sense. Real
competence is recall plus synthesis — sitting in front of a blank main.py
and knowing, without prompting, that the create endpoint returns 201 with a
Location header, that the rate limiter goes outside auth, that the service
raises a domain exception instead of an HTTPException, that the sort
parameter must be whitelisted, and that all of it should produce a truthful
OpenAPI spec.
Everything in this track was deliberately building one project up module over module: validation (00–02), middleware (03–04), request context (05), layering (06), CRUD (07), REST design (08), and OpenAPI (09). The capstone asks you to assemble a coherent API where those pieces fit together the way they do in a real service — not eight disconnected demos, but one system whose layers reinforce each other. If a piece feels shaky when you try to place it, that's the signal to go back to that module's exercises and redo one from memory. Struggling here, before you look anything up, is the entire point.
The project
Build a complete REST API for a small resource of your choice — a task
list or an order API are the intended targets, but any single primary
resource with a couple of related sub-resources works (a bookmarks API, a
notes API, a simple inventory). Use FastAPI + Pydantic. Start from an
empty folder; do not copy a previous module's main.py wholesale — rebuild it
so the recall is real. No solution code is given.
Your API must bring together the whole track:
Resource & REST design (module 08)
- Model the primary resource and at least one nested/related sub-resource
(e.g.
tasksandtasks/{id}/comments, orordersandorders/{id}/items). - Use correct HTTP methods and status codes throughout (
201+Locationon create,200/404on read,204on delete,409on conflict). - Version the API under a
/v1prefix from the start. - Express at least one non-CRUD operation as a state change, not a verb URL
(e.g. "complete a task" / "cancel an order" via
PATCH). - Add ETag-based revalidation (
304) to the single-resource GET.
Validation & transformation (modules 00–02)
- Strict Pydantic validation on every write: type, syntactic, and semantic
rules, with at least one custom
@field_validatorand one cross-field@model_validator. - Normalize at least one field (trim/lowercase) before storage.
- Aggregate validation errors and return them in a consistent envelope.
- Never leak internals or enable enumeration in error messages.
Middleware chain (modules 03–04)
- At least 3 custom middlewares in a deliberately ordered chain. Recommended: structured request logging (outermost), a request-ID middleware, rate limiting, and security headers — plus a catch-all error handler. Order must be defensible: logging outside auth, rate limiting outside auth, error handling outermost.
- Document (in a comment or README) why your order is correct.
Request context & layering (modules 05–06)
- Inject the current user and a per-request "DB session" (a
yielddependency) viaDepends; no module-level globals for per-request data. - Clean handler → service → repository layering. Services raise domain exceptions and import no FastAPI. Centralized exception handlers map every domain error to the shared error envelope.
- Require auth via a router-level dependency so no endpoint is exposed by accident.
CRUD features (module 07)
- Full CRUD on the primary resource.
- Pagination with a capped page size (offset is fine; cursor earns extra credit on the collection you expect to grow).
- Filtering, sorting (with a whitelist), and a search (
q) parameter on the collection. - Redact sensitive/internal fields via dedicated
*Outmodels.
OpenAPI (module 09)
- A rich, accurate auto-generated spec:
infometadata, tag groups, per-route summaries, documented non-default status codes, described schemas with examples, and a documented bearer-auth security scheme. /docs,/redoc, and/openapi.jsonall reflect the real behavior.
Acceptance checklist
Tick every box. If you can't, you've found a module to revisit.
-
POST /v1/<resource>returns201with aLocationheader and the created body; invalid input returns an aggregated422; a duplicate/ conflict returns409. -
GET /v1/<resource>/{id}returns200with an ETag; a repeat request withIf-None-Matchreturns304; a missing id returns404. -
GET /v1/<resource>paginates with a capped limit, supports filtering, whitelisted sorting, and aqsearch; an unknown sort field returns400; an oversized limit returns422. -
PATCHapplies only the fields sent (no clobbering);DELETEreturns204; a state-change operation is modeled as a resource update, not a verb URL. - A nested sub-resource endpoint works and is scoped to its parent.
- At least 3 custom middlewares run in a defensible order; a rate-limited
request is still logged and still gets security headers; an unhandled
exception becomes a clean
500with no stack trace in the body. - Every request carries a request ID that appears in logs and in the response headers and error envelope.
- The current user is injected via DI, comes from
request.state/a dependency (never a global), and every endpoint requires auth via a router-level dependency. - Services contain the business rules, import no FastAPI, and raise domain exceptions; one central place maps them to HTTP; validation and domain errors share one envelope.
- No response leaks an internal/sensitive field (verified against your
*Outmodels). -
/docsshows grouped, documented endpoints with an Authorize button;/openapi.jsonaccurately describes every request schema, response schema, and status code you actually return. - You can unit-test at least one service method with plain Python, no web server.
Suggested build order
- Sketch the contract first (module 09 mindset): resources, URLs, methods, status codes, schemas — on paper or as a draft spec — before writing handlers.
- Build the layers bottom-up: repository (in-memory is fine), then service with domain exceptions and rules, then thin handlers.
- Add the Pydantic schemas (
*Create,*Update,*Out) with validators and normalization. - Wire the middleware chain and prove the order with a trace.
- Add pagination/filter/sort/search and ETags.
- Enrich the OpenAPI metadata and verify
/docstells the truth. - Run the acceptance checklist as an adversary trying to break each item.
Hints (design nudges, not solutions)
Hint: getting the middleware order right
Reason outer→inner: error handling (outermost, catches everything) → logging
(sees every request including rejected ones) → request ID → rate limiting
(drops abuse before auth spends CPU) → auth → handler; security headers/
compression apply on the way out. Remember that with add_middleware the
last-registered is the outermost, and that short-circuit responses (a 429, a
401) still travel outward through every outer middleware — which is exactly
why logging and security headers still apply to them.
Hint: keeping services testable
If your service module imports anything from fastapi, a rule leaked into the
wrong layer. Services take plain arguments (including user_id, passed down by
the handler after it resolves the user via Depends) and raise domain
exceptions. Then a plain-Python test can construct the service with a fake
repository and assert on behavior with no server, no routing, no HTTP.
Hint: making the OpenAPI spec truthful
The spec is a mirror of your declarations. Every response_model, every
status_code, every responses={404: ...}, every Field(description=, examples=), and every security utility you attach shows up in /openapi.json
automatically. Define one ErrorResponse model for your envelope and $ref
it everywhere via responses= so the error shape is documented once in
components.schemas.
Further reading & sources
- FastAPI — Full Tutorial (User Guide) - the end-to-end reference covering every building block this capstone integrates.
- FastAPI — Bigger Applications - structuring a multi-router, versioned app cleanly for a project this size.
- Pydantic — Models - the schema and validation foundation for your
*Create/*Update/*Outmodels. - MDN — HTTP response status codes - the authoritative status-code reference for verifying your CRUD and REST semantics.
- OpenAPI Specification (spec.openapis.org) - confirm your auto-generated contract matches the standard.
- OWASP API Security Top 10 - a checklist of the API risks (broken auth, excessive data exposure, injection) your safeguards defend against.
Next
You've finished the API layer. Next is track 03, where every endpoint you just learned to build gets a real identity and permission model behind it: ../../03-authentication-and-authorization/README.md — sessions, JWTs, OAuth2/OIDC, API keys, MFA, and RBAC, plus the common auth security holes to avoid.