REST Best Practices
Objectives
By the end of this lesson, you should be able to:
- Explain idempotency, and which HTTP methods should and shouldn’t be idempotent
- Apply a consistent response envelope and error format across an entire API
- Identify a handful of practices that separate a well-designed API from one that merely works
💡 Why this matters: Every lesson in this module covered one specific convention, this lesson ties them together, plus a few final practices, into the complete picture of what makes a REST API genuinely pleasant to build against.
⚠️ A note on verification: every snippet and every response shown below was actually run and tested with real HTTP requests.
Idempotency
An operation is idempotent if performing it multiple times produces the same result as performing it once. GET, PUT, and DELETE should all be idempotent, POST should not:
curl -X POST http://localhost:4506/items -H "Content-Type: application/json" -d '{"name":"Widget"}'
curl -X POST http://localhost:4506/items -H "Content-Type: application/json" -d '{"name":"Widget"}'
{"id":1,"name":"Widget"}
{"id":2,"name":"Widget"}
curl -X PUT http://localhost:4506/items/50 -H "Content-Type: application/json" -d '{"name":"Gadget"}'
curl -X PUT http://localhost:4506/items/50 -H "Content-Type: application/json" -d '{"name":"Gadget"}'
curl -X PUT http://localhost:4506/items/50 -H "Content-Type: application/json" -d '{"name":"Gadget"}'
{"id":50,"name":"Gadget"}
{"id":50,"name":"Gadget"}
{"id":50,"name":"Gadget"}
Two identical POST requests correctly created two separate items (id: 1 and id: 2), creating something is inherently a “do this again” operation. Three identical PUT requests to the same URL, by contrast, leave the exact same end result every time, id: 50 with name: "Gadget", repeating it changes nothing further. This matters practically: a client that isn’t sure whether a PUT request actually reached the server can safely retry it, retrying a POST the same way risks creating duplicates.
A Consistent Response Envelope
Lesson 5’s pagination already established a pattern worth generalizing: a collection response wraps its data ({ data: [...], pagination: {...} }), rather than returning a bare array. A single-resource response, by contrast, is conventionally returned directly, { id: 1, name: "Erin" }, not wrapped further, this asymmetry is intentional, a single resource has no metadata (a page number, a total count) that needs wrapping. Whichever convention an API picks, applying it consistently across every endpoint is what actually matters, a client integrating against the API shouldn’t have to guess whether this particular endpoint wraps its response or not.
A Consistent Error Format
Module 8 already established this: every error response, from every endpoint, should follow the same shape, { error: "ErrorType", message: "..." }, or similar, applied everywhere. A client handling errors from one endpoint should be able to reuse that exact same handling logic for every other endpoint in the same API, this is only true if the error shape never changes from one route to the next.
A Few Final Practices
Use the correct status code for every outcome (Module 5, Module 8), don’t default everything to 200 or 500. Validate input early, and reject it with a clear 400 before any real work happens (Module 8’s ValidationError). Document what an API actually does, even a short description of each endpoint, its parameters, and its response shape saves far more time than it costs. Consider rate limiting for a public API (Module 7’s middleware pattern is exactly the mechanism a rate-limiting package would use), preventing a single client from overwhelming the server. None of these are unique to this module, they’re the same conventions from Modules 5 through 9, applied consistently across an entire API rather than to individual routes in isolation.
Try It
- Test an API you’ve built in this course for idempotency: send the same
PUTrequest three times, and confirm the result is identical each time. - Audit your own API’s responses (or a public API’s documentation) for envelope consistency, do collection endpoints and single-resource endpoints follow a predictable pattern?
- Audit the same API’s error responses, do they all share the same shape, or does the format vary by endpoint?
- Pick one practice from this lesson not yet applied consistently across your own project, and fix it.
Recap
GET,PUT, andDELETEshould be idempotent, repeating them produces the same end result,POSTshould not, each call creates something new.- A consistent response envelope (wrapped collections, unwrapped single resources) and a consistent error format across every endpoint are what make an API predictable to build against.
- Correct status codes, early validation, documentation, and rate limiting round out a genuinely well-designed REST API, all built from conventions already covered across Modules 5 through 9.
Next lesson: this module’s exercises, designing and building a complete, properly versioned REST API.