Capstone: API Design Review and Production Checklist

Advanced
16 min

Capstone: API Design Review and Production Checklist

A teammate has drafted the API for an order service and asked for a review before the frontend team builds against it. This capstone walks through that review the way a senior engineer would: spot each violation of what you learned in this course, name the principle behind the fix, and rewrite the design, ending with a production checklist.

The Draft Under Review

http
GET /getAllOrders?token=abc123 -> 200 [ ...every order in the database... ] POST /createOrder -> 200 { "success": true, "orderId": 17 } POST /deleteOrder?id=17 -> 200 { "success": false, "error": "not found" } POST /orders/17/setStatus {"status":1} -> 500 "TypeError: Cannot read properties..." GET /orders/17 -> 200 { ..., "customer": { "passwordHash": "..." } }

Review Findings

| Finding | Principle | Fix | |---|---|---| | Verbs in URLs (/getAllOrders) | Resources are nouns; methods are the verbs | GET /v1/orders, DELETE /v1/orders/17 | | Token in the query string | Credentials belong in headers; URLs get logged | Authorization: Bearer <token> | | 200 with "success": false | Status codes carry the outcome | 404 with an application/problem+json body | | Returns every order at once | Collections are paginated and filterable | ?status=&limit=&cursor= with links.next | | Anyone can read any order | Object-level authorization (OWASP API1) | Filter by ownerId | | passwordHash in the response | Property-level authorization (OWASP API3) | Explicit serializers; never dump ORM rows | | Stack trace on bad input | Validate first; never leak internals | 422 with field errors; generic 500 | | POST /setStatus action | State changes are updates to the resource | PATCH /v1/orders/17 { "status": "shipped" } |

The Corrected Endpoint

The rewritten list endpoint applies the findings: scope check, ownership filter, cursor pagination, a next link, ETag revalidation, and errors forwarded to the Problem Details middleware:

javascript
router.get("/orders", requireScope("orders:read"), async (req, res, next) => { try { const { status, limit = 20, cursor } = req.query; const page = await orders.list({ ownerId: req.auth.sub, status, limit: Math.min(Number(limit), 100), cursor }); const body = { data: page.items, links: { self: req.originalUrl, next: page.nextCursor ? `/v1/orders?limit=${limit}&cursor=${page.nextCursor}` : null }, }; const etag = etagFor(body); res.set("ETag", etag); if (req.get("If-None-Match") === etag) return res.status(304).end(); res.json(body); } catch (err) { next(err); } });
json
{ "data": [{ "id": "ord_17", "status": "shipped", "total": 2499 }], "links": { "next": "/v1/orders?limit=20&cursor=eyJpZCI6Im9yZF8xNyJ9" } }

Production Checklist

Design: nouns in URLs, correct methods and status codes, pagination on every collection, /v1 versioning, consistent naming.

Security, mapped to the OWASP API Security Top 10: ownership checks on every object (API1); short-lived tokens and PKCE (API2); explicit serializers and input whitelists (API3); rate, body-size and pagination limits (API4); role checks on admin functions (API5); idempotency keys on sensitive flows (API6); never fetch client-supplied URLs unvalidated (API7); HTTPS, strict CORS, no stack traces (API8); an inventory of every version (API9); validate third-party data like user input (API10).

Reliability and operability: idempotent retries, 202 for long jobs, outbound timeouts, a health endpoint, request ids in logs, metrics per endpoint, a published OpenAPI document, tests in CI.

Beyond REST

| | REST | GraphQL | gRPC | |---|---|---|---| | Shape | Resources at URLs | Client-defined queries, one endpoint | Typed procedures over HTTP/2 | | Contract | OpenAPI (optional) | Schema (required) | .proto files (required) | | Caching | Native HTTP caching | Hard; mostly client-side | None built in | | Best for | Public APIs, CRUD, browsers | UIs aggregating resources | Internal services, streaming |

REST is the default for public APIs; GraphQL suits flexible aggregation and gRPC high-volume internal traffic.

Quick Quiz
Question 1 of 2

The draft returns `200 { "success": false }` for a missing order. What is the correct response?

Key Takeaways

  • Review an API against principles, not taste: resource URLs, method semantics, status codes, pagination, versioning.
  • Security review starts with object- and property-level authorization, then tokens, limits and config.
  • Errors are application/problem+json, collections are paginated with links, and reads carry ETags.
  • Publish the OpenAPI document and back it with integration and contract tests before calling it done.

What to learn next: continue with the GraphQL course, explore gRPC for internal services, and study API gateways and observability tooling.

Capstone: API Design Review and Production Checklist - REST APIs | CodeYourCraft | CodeYourCraft