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=50is "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"
}
123is in the path because you must say which event you're booking.notify=trueis 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.