Unit 4.8 · about 5 h
26 / 324HTTP APIs, pagination, streaming and idempotency
Retries are normal and duplicates are corruption. Validation plus status codes make failures legible, pagination bounds the response, and idempotency keys make a retried request safe to repeat.
- explain
- implement
You can now
- Expose the service over HTTP with request validation, response filtering, correct status codes and typed errors.
- Ship capped
limit-offsetpagination and one streaming endpoint with bounded pages. - Implement idempotency keys so retried POSTs return the stored response with a single side effect, and mismatched replays error.
Covers
- HTTP methods, validation, status codes, pagination and streaming
- Idempotency keys, duplicate replay and side-effect ownership
Study, in this order
8 steps
- Request BodyFastAPI tutorial
Create your data model; Declare it as a parameter; Results; Use the model
- Handling ErrorsFastAPI tutorial
Use
HTTPException; Install custom exception handlers; Override request validation exceptions - Response ModelFastAPI tutorial
Return the same input data (the leak); Add an output model; Return Type and Data Filtering
- Response Status CodeFastAPI tutorial
Changing the default, with
201for creates; Shortcut to remember the names - Numeric validations for a page capFastAPI tutorial
Number validations: greater than and less than or equal (
geandlework the same onQuery, which is howlimitgets a cap) - Stream JSON LinesFastAPI tutorial
Stream JSON Lines with FastAPI: yield items from a function returning
AsyncIterable[Item](added in FastAPI 0.134.0); Non-async path operation functions; No Return Type - Idempotent requestsStripe idempotent requests
Idempotency-Keyheader with V4 UUID keys up to 255 characters; for API v1 the status code and body are saved once endpoint execution begins and replayed on retry, including500s, while validation failures and concurrent conflicts save nothing; parameter-mismatch errors; keys onPOST, neverGETorDELETE; keys pruned after at least 24 hours
Focus on
- One Pydantic model per request, one output model per response. Invalid bodies fail with
422before any business logic runs. - The output model strips secrets: returning the input model leaks fields. Create returns
201, unknown ids return404with thedetailenvelope. - Defaults alone do not bound a page: a client can still send
limit=1000000. Cap it withQuery(le=...)so oversized requests fail with422. Streams yield items under a declaredAsyncIterable[Item]return type so validation and docs still apply. - The idempotency ledger stores, per key, the endpoint, a hash of the params and the saved response. A replay with the same hash returns the saved response; the same key with a different hash errors. Keys belong on
POST:GETandDELETEare idempotent by definition.
Reproduce closed-book
idempotency-key ledger: show 201 with secrets stripped, 422 on a bad body and on an oversized limit, 404 on an unknown id, a bounded page slice, and a double POST with one key producing one stored object.Traps
- Returning the input model. Extra fields and secrets serialize to the client; a separate output model filters them, verified with
TestClienton FastAPI 0.143. - Same idempotency key with new params silently accepted. Compare params and return a conflict, or a retry becomes an overwrite.
- Check-then-insert on the ledger. Two concurrent first attempts both miss the lookup and both run the side effect; claim the key with a unique insert first, and answer a concurrent duplicate with a retryable conflict.
Practice
FastAPI tutorial Testing: extended example
Testing extended example: extended app file plus extended testing file, Run it
In that style, write replay, mismatch and oversized-limit tests for your keyed endpoint plus a pagination slice test, swapping the key store with app.dependency_overrides (Advanced User Guide: Testing Dependencies with Overrides).
Self-test
-
A
POSTbody misses a required field. What status, and when does business logic run?422from validation. Business logic never runs. Verified withTestClienton FastAPI 0.143. -
A client
POSTs twice with the same key after a network timeout. How many objects exist and what does the second response carry?One object. The second response replays the saved status code and body. Stripe saves once endpoint execution begins, so a saved500replays too, while a request that failed validation saves nothing and can be retried. -
The same key arrives with different params. What happens?
The server errors on the mismatch instead of applying the second params. -
GET /itemswould return 100000 rows. What breaks and what is the fix?Memory, latency and client parsing break. Page withskipandlimitand caplimitwithQuery(le=...): a default alone does not stoplimit=1000000. Or stream JSON Lines with a declared iterable type. Verified on FastAPI 0.143.
Completion criterion
Stop when
TestClient shows 201 filtered, 422 on a bad body and an oversized limit, the 404 detail envelope, a bounded page, and a keyed double POST with one side effect plus a mismatch conflict.
Leads to
- stage 06 · Systems Foundations Carry pagination and streaming into storage and network behavior.
Comes after
- 4.5 Transactions, isolation anomalies and ORM boundariesRetried requests need transaction boundaries that make replay safe.
- 4.6 Indexes, selectivity and query plansIndex the
idempotency-keyand pagination lookups behind the endpoints. - 4.7 Layer boundaries and command-line contractsExpose the same service over HTTP with identical outcomes.
Resources for this unit
- FastAPI tutorial Use Request Body, Dependencies, Handling Errors and Testing to expose the same service layer through HTTP. Keep framework models outside the domain core and preserve the same error semantics as the CLI.
- Stripe idempotent requests Use the
Idempotency-Keyheader contract, stored-response replay, parameter-mismatch errors andPOST-only scope to implement keyed replay for one endpoint.
Also in this section
- Official docs typed-service build path Build one record service in this order: structural protocols and generics; Pydantic models, validators, strict mode, serialization and settings; FastAPI request bodies, dependencies, errors and tests; then PostgreSQL transactions and EXPLAIN. Replay valid, malformed, duplicate and previous-schema requests through CLI and HTTP.
- AlgoMaster SQL - selected route Study SQL Foundations, Querying Data, Joins, Subqueries and CTEs, Window Functions, DDL, DML and Views, Performance Optimization, Transactions and Concurrency, and Data Modeling. Stop when the service queries and transaction tests pass.
- PostgreSQL transactions and EXPLAIN Use the transaction tutorial and EXPLAIN guide to demonstrate commit, rollback, one isolation anomaly or prevented anomaly, and a measured plan improvement on the database actually used.
- AlgoMaster Low-Level Design - one rehearsal Use one fitting design prompt to rehearse interface contracts, dependency direction and failure ownership. Treat the review as feedback, then verify behavior with your own tests.
- Stas' Python Cookbook - data and interfaces Use Serialization and Deserialization plus Environment and Arguments for focused implementation help. Compare JSON, JSONL, CSV and compressed data, and never use pickle for untrusted interchange.
- AlgoMaster Python - interfaces, generics and data files Use Interfaces and Protocols plus Generic Types for structural typing, and Working with JSON plus Working with CSV for JSONL, error handling,
newlineandDictReaderbehavior. Lesson quizzes check recall; implementation is verified with your own scripts. - SQLAlchemy 2.1 documentation - select, Session and eager loading Use the
select()tutorial for 2.x query style,Sessionbasics for the transaction boundary and identity map, and relationship loading for lazy versusselectinloadversusjoinedloadplusraiseload. Useselect()style andSession.executeorSession.scalarsonly, never the legacyQueryAPI. - Alembic tutorial - migration scripts and downgrading Use Create a Migration Script, Running our First and Second Migration, Getting Information and Downgrading to run a paired
upgradeanddowngraderound trip on a scratch database. - PyArrow Parquet read and write docs Use Reading and Writing Single Files plus
Tablefrom_pydictandfrom_pylistto convert normalized records to Parquet and test types, nulls, column projection and schema.