APIs
REST modeling: design an order API that survives change
Reading about resource modeling is not the same as defending a contract through a real schema change. Design and build a small order API, then prove it survives the three things that break naive APIs in production: a column rename, an unguarded state transition, and a retried create.
Turn the unit’s mental model into a working API: model nouns instead of verbs, express non-CRUD actions as guarded transitions, decouple the representation from the schema with a mapping layer, and make creates retry-safe — proving each property, not asserting it.
Design and build a small order-management REST API (Orders, Items, Refunds) whose contract survives a database column rename, refuses an illegal state transition, and returns the original result on a retried create — demonstrating each property with a request/response transcript.
- A transcript proving schema decoupling: rename a DB column (e.g. full_name to display_name) with NO change to the wire representation, shown by identical request/response before and after the migration.
- A transcript proving the guarded transition: POST /orders/{id}/cancel succeeds on a cancellable order and is rejected with a 4xx on an already-shipped one, while a direct attempt to write status via PATCH is refused.
- A transcript proving retry safety: the same POST sent twice with one Idempotency-Key creates exactly one resource and returns the same id/result both times.
- A one-page URL map and a short write-up naming, for each non-CRUD action, whether you chose an action sub-resource or a transition resource and why.
- Add cursor-based pagination, filtering, and sorting on the collection (GET /orders?status=open&sort=-created_at&cursor=...) and document why these are query params, not new endpoints.
- Add HATEOAS links (_links with the legal next actions per order state) and write one client that follows the cancel link instead of hardcoding the URL — then note what it cost versus level-2 REST.
- Add API versioning and demonstrate evolving a representation without breaking v1 clients, absorbing the change in the mapping layer.
- Add an OpenAPI spec generated from the implementation and a contract test that fails the build if a response leaks a field not in the documented representation.
This is the design loop you run on every real API: model nouns and let the method carry the verb; express non-CRUD transitions as guarded actions or transition resources so the server owns the state machine; keep nesting shallow and expand instead of walking; serialize through a mapping layer so the representation is a contract you own and the database stays refactorable; and make creates retry-safe with idempotency keys. Doing it once on a small order API turns these from rules you can recite into shapes you reach for by reflex.