CodingNic

REST API Design

Exercises

REST API Design 35 min read

Exercises

Objectives

By the end of this lesson, you should be able to:

  • Design and build a complete, versioned REST API from scratch
  • Combine pagination, filtering, sorting, and search on a single collection endpoint
  • Apply a consistent envelope and error format across every route

⚠️ A note on verification: every command and output in this lesson was actually run with Express.

Exercise: A Books API

a) Seed data and versioned router. Using this seed data, mounted under /api/v1 with express.Router():

javascript
let books = [
  { id: 1, title: 'Clean Code', genre: 'programming', price: 35 },
  { id: 2, title: 'Dune', genre: 'sci-fi', price: 20 },
  { id: 3, title: 'Refactoring', genre: 'programming', price: 40 },
  { id: 4, title: 'Foundation', genre: 'sci-fi', price: 18 }
];

b) Full CRUD. Build GET /api/v1/books/:id, POST /api/v1/books (with title validation, 400 if missing), PUT /api/v1/books/:id (full replace), PATCH /api/v1/books/:id (partial update), and DELETE /api/v1/books/:id (returning 204). Every error response should follow the same { error: "...", message: "..." } shape.

bash
curl -w " [%{http_code}]" "http://localhost:4507/api/v1/books/99"
text
{"error":"NotFound","message":"Book not found"} [404]

c) Paginated, filterable, sortable collection. Build GET /api/v1/books supporting genre (exact filter), q (case-insensitive search on title), sort (with -field for descending), page, and limit (capped at 50), all combinable in a single request, returning a { data: [...], pagination: {...} } envelope.

bash
curl "http://localhost:4507/api/v1/books?genre=programming&sort=-price&page=1&limit=1"
text
{"data":[{"id":3,"title":"Refactoring","genre":"programming","price":40}],"pagination":{"page":1,"limit":1,"totalItems":2,"totalPages":2}}

d) Idempotency check. Send the same PUT request to one book three times in a row, and confirm the result is identical every time, then send the same POST request twice, and confirm two separate books are created instead.

e) A v2 with a breaking change. Add a second router mounted at /api/v2, with GET /books/:id returning a deliberately different shape (renaming title to name, for example), and confirm /api/v1/books/:id is completely unaffected.

f) Test everything. Use curl to test every route, every query parameter combination, both success and error cases, for both v1 and v2.

Recap

This module covered designing, not just building, a REST API: the core principles (resources, statelessness), the CRUD-to-HTTP-method mapping (including the real difference between PUT and PATCH), consistent resource naming and nesting, versioning to evolve an API safely, pagination, filtering, sorting, and search on collection endpoints, and the final practices, idempotency, consistent envelopes and error formats, that tie every earlier convention together into a genuinely well-designed API.

Next module: organizing everything built so far into a properly structured Express application.