bigonotes
System DesignAPI Design

REST

Model resources from your core entities, use HTTP methods correctly, and pass data via path, query and body.

3 min readLast updated API Design

REST (Representational State Transfer) models your system as resources that clients act on with standard HTTP methods. It's the default API style for web and mobile backends because it maps cleanly onto your core entities and onto HTTP itself. Good REST design comes down to three things: picking the right resources, using methods correctly, and putting each piece of data in the right part of the request.

Resource modeling#

Resources are the core entities you identified earlier: users, events, tickets, bookings, comments. Name them as plural nouns.

For a ticketing system with entities Events, Tickets, Bookings and Venues:

Endpoint What it does
GET /events List events
GET /events/{id} Get one event
GET /venues/{id} Get one venue
GET /events/{id}/tickets List available tickets for an event
POST /events/{id}/bookings Create a booking for an event
GET /bookings/{id} Get one booking

Interviewers care more about whether you pick the right resources than about perfect URLs. Still, structuring relationships well shows good design sense.

Nested resources (path) vs flat resources (query)#

  • Nest in the path when the relationship is required and hierarchical. A ticket can't exist without an event, so /events/{event_id}/tickets. Remove the event id and the URL makes no sense.
  • Use query parameters when the relationship is optional or used for filtering. /tickets?event_id=123&price_max=50 is "all tickets, narrowed down". Drop the parameters and it still works; it just returns everything.

A good way to explain it: "event_id goes in the path because a ticket can't exist without an event. The price range is a query parameter because it's an optional filter, not part of what the resource is."

HTTP methods#

Each HTTP method has a fixed meaning, and clients rely on it.

Method Use Example Idempotent?
GET Read without changing anything GET /events/{id} Yes
POST Create; the server assigns the id POST /events/{id}/bookings No: calling it twice creates two bookings
PUT Replace a whole resource PUT /users/{id} with the full user Yes
PATCH Update part of a resource PATCH /users/{id} with just email Not guaranteed
DELETE Remove DELETE /bookings/{id} Yes: it stays deleted, even if later calls return 404 instead of 204

Idempotency means repeating a request leaves the server in the same state. It matters because networks fail and clients retry. Retrying a GET, PUT or DELETE is safe; retrying a POST can create duplicates unless you add an idempotency key.

Passing data to an API#

A request can carry data in three places, and each has its own role.

Where Role Example
Path parameters Structural: which resource /events/123
Query parameters Modifiers: filter, sort, paginate /events?city=NYC&date=2024-01-01, /events?page=2&limit=20
Request body Payload: the data you're creating or updating Booking details, anything large or sensitive

The first query parameter follows ?, and the rest are separated by &.

All three together:

POST /events/123/bookings?notify=true
Content-Type: application/json

{
  "tickets": [
    {"section": "VIP", "quantity": 2},
    {"section": "General", "quantity": 1}
  ],
  "payment_method": "credit_card"
}
  • 123 is in the path because you must say which event you're booking.
  • notify=true is a query parameter because it's optional behavior.
  • The tickets and payment method go in the body because they're the data being created.

Returning data#

A response has two parts: a status code (did it work?) and a body (usually JSON).

Stick to the common codes: 200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Internal Server Error. See HTTP Status Codes for a fuller table.

Interviewers care more that you know the difference between client errors (4xx) and server errors (5xx) than that you've memorized every code.

Key takeaways#

  • Resources come from your core entities. Use plural nouns, not verbs.
  • Use path parameters for required hierarchy and query parameters for optional filters.
  • GET, PUT and DELETE are idempotent; POST isn't, so guard critical POSTs against retries.
  • Path identifies, query modifies, body carries the payload.
  • Use a small set of status codes and know 4xx from 5xx.

Found a mistake? Tell me on LinkedIn ↗

Further reading#