Fundamentals of RESTful API Design
Practical rules for making an API predictable through the triangle of resources, HTTP methods, and status codes.
At some point I needed to add a mobile app or another client to my Laravel projects, so I naturally started writing APIs. My first APIs looked something like this: /user/fetch, /user/save, /order/list. They worked, but as the number of URLs grew it became increasingly hard to keep track of what each one actually did.
REST (Representational State Transfer) offers an established approach for avoiding that chaos. In this post I’ll cover the three fundamental building blocks of REST — resource, HTTP method, and status code. Understanding them is both sufficient and the easiest-to-overlook foundation for making an API predictable.
What is a Resource?
In REST, everything is a resource. A resource represents an entity in your application: a user, an order, a product, a comment, and so on. Resources are addressed via URLs.
Resource URLs are noun-based — they never contain verbs:
# Wrong
GET /fetch-users
POST /create-new-user
# Right
GET /users
POST /users
Using plural nouns is the common convention: /users, /orders, /products.
To access a specific instance of a resource, append its identifier to the URL:
GET /users/42 → user with ID 42
GET /orders/7 → order with ID 7
For related resources, use a nested structure:
GET /users/42/orders → orders belonging to user 42
HTTP Methods
While the URL identifies the resource, the HTTP method tells the server what to do with it. There are five primary methods:
| Method | Meaning | Example |
|---|---|---|
| GET | Read the resource | GET /users/42 |
| POST | Create a new resource | POST /users |
| PUT | Fully replace the resource | PUT /users/42 |
| PATCH | Partially update the resource | PATCH /users/42 |
| DELETE | Delete the resource | DELETE /users/42 |
Using these methods correctly makes an API predictable for anyone already familiar with REST. When you call GET /users/42 you know nothing on the server should change. When you call DELETE /users/42 it’s obvious what to expect.
In the old HTML-form world we only ever used GET and POST. In a REST API, leveraging all the available methods makes intent explicit.
Status Codes
An HTTP status code tells the client how the server responded to the request. The standard defines a few hundred codes, but in practice you only need to know a small subset.
Successful responses (2xx):
200 OK— Request succeeded, data is returned201 Created— Resource was successfully created204 No Content— Successful, but there is nothing to return (typically after a DELETE)
Client errors (4xx):
400 Bad Request— The submitted data is malformed or incomplete401 Unauthorized— Authentication is required403 Forbidden— Authenticated, but not authorized404 Not Found— The requested resource does not exist422 Unprocessable Entity— Data format is valid, but content failed validation
Server errors (5xx):
500 Internal Server Error— An unexpected error occurred on the server side
Using these consistently matters. Returning 200 OK for every response and burying "error": true in the body technically works, but it goes against REST principles and makes life harder when you’re working with HTTP tooling (Postman, curl, browser extensions) because you have to parse the body to understand what actually happened.
A Real-World Example
A simple API design for an orders resource might look like this:
GET /orders → List all orders → 200 OK
POST /orders → Create a new order → 201 Created
GET /orders/7 → Fetch order 7 → 200 OK
PUT /orders/7 → Update the order → 200 OK
DELETE /orders/7 → Delete the order → 204 No Content
Five URLs and five methods cover all CRUD operations. Anyone familiar with this pattern can guess what the API is capable of without ever reading the code.
Why Does This Matter?
There is no requirement to follow REST conventions — PHP certainly won’t stop you. But for whoever is writing an API client (a mobile app, another service, a frontend), predictability is a huge quality-of-life improvement. Tools like Postman and Swagger also work significantly better with APIs that follow REST conventions.
Once I internalized all of this, I went back to my old /user/fetch-style URLs and fixed them one by one. It took a bit of time, but the confusion on the consumer side disappeared.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.