Module 3 · API design

Lesson 13 — REST done right

Resources, verbs, consistent errors, pagination, filtering and ordering.

Published
In this lesson
  1. Exercise 1 — The full surface
  2. Exercise 2 — RFC 7807 handler
  3. Exercise 3 — Real pagination
  4. Exercise 4 — Public UUID
  5. Exercise 5 — The header contract: ETag, Vary and platform headers
  6. Submit

DRF on your project. Do not look at solutions.md before submitting.

Exercise 1 — The full surface

  1. Write TicketFlow's complete route table (like the lesson's) including: register/login (a preview of 18), events, seats, availability, reservations, payments, webhook, gift cards.
  2. For each route: verb(s), required auth (public / owner / organizer / system), and the success code.
  3. Critique your own table: any verb in the path? Any nesting >1 level? Any non-plural?

Exercise 2 — RFC 7807 handler

  1. Implement custom_exception_handler(exc, context) in DRF producing the lesson's format for: ValidationError (400), Http404 (404), PermissionDenied (403), and your domain exception (409/422).
  2. Define DomainError(code, status, title, detail) and raise it from Lesson 10's reserve() when the seat is taken; verify the resulting JSON body.
  3. What happens with detail: None on errors without detail? Decide the mandatory minimum fields and justify it.

Exercise 3 — Real pagination

  1. Configure cursor pagination on the events listing and offset on the admin panel (DRF settings: two paginators). Verify the responses (next, previous, results).
  2. Generate 500 events and compare ?page=999 (offset) vs a deep cursor: measure times with EXPLAIN ANALYZE underneath.
  3. Add filters with django-filter: state, starts_after, ordering with a whitelist (only starts_at, title, -starts_at).

Exercise 4 — Public UUID

  1. Add uuid = UUIDField(default=uuid4, unique=True, editable=False) to Event and Reservation (a safe Lesson 11 migration).
  2. Switch lookups to lookup_field = "uuid" in your viewsets and serializers. Verify that /api/events/{integer}/ returns 404 and the UUID 200.
  3. Should user A's reservation be enumerable by user B with the right UUID? Which layer prevents it (preview of 21)?

Exercise 5 — The header contract: ETag, Vary and platform headers

  1. Add an ETag to the event detail (from the resource's updated_at, Lesson 38) and verify: a second request with If-None-Match → empty 304. What does it save on spike day?
  2. Add Vary: Accept-Encoding to the proxy or the response and check with curl that the 304 distinguishes a gzip client from a plain one (39).
  3. Platform headers: add to EVERY response X-Request-ID (Lesson 45's trace_id), X-RateLimit-Remaining (23) and X-Content-Type-Options: nosniff. Contract test: all three present on a 200 and on a 409.

Submit

Paste routes, handler code and outputs. Next: Lesson 14 — Idempotency and versioning.