Resources are Nouns
Use nouns for resources, HTTP methods for actions. /users with GET, not /getUsers.
REST (Representational State Transfer) is an architectural style for designing web APIs that was introduced by Roy Fielding in his 2000 doctoral dissertation. REST provides a set of constraints and principles that, when followed, create APIs that are scalable, maintainable, and easy to understand.
REST is built on the idea that web APIs should work like the web itself—using standard HTTP methods to manipulate resources identified by URLs. Instead of creating custom protocols or action-based endpoints, REST uses the existing HTTP infrastructure in a standardized way.
Key Insight: REST treats everything as a resource—a noun that can be identified by a URL. Actions are performed using standard HTTP methods (verbs). This separation of concerns makes APIs predictable and intuitive.
Resources are the core of REST. A resource is anything that can be identified and manipulated.
Good resources are:
/users, not /getUsers) - Resources represent entities, not actions/users/123/orders) - Reflect relationships between resources/users, not /user) - Collections are plural, individual resources are identified by ID| Good | Bad | Why? |
|---|---|---|
/users | /getUsers | Verbs in URL violate REST principles. Use GET method instead. |
/users/123 | /user/123 | Inconsistent plural. Collections should be plural for clarity. |
/users/123/orders | /orders?userId=123 | Better hierarchy. Shows relationship between user and orders. |
/products/456 | /product?id=456 | Resource identifier should be in path, not query parameter. |
HTTP methods define what action to perform on a resource.
| Operation | HTTP Method | Idempotent? | Safe? | Use Case |
|---|---|---|---|---|
| Create | POST | No | No | Create new resource |
| Read | GET | Yes | Yes | Retrieve resource |
| Update (Full) | PUT | Yes | No | Replace entire resource |
| Update (Partial) | PATCH | No* | No | Update part of resource |
| Delete | DELETE | Yes | No | Remove resource |
*PATCH can be idempotent if designed correctly
GET is for reading data. It’s safe (no side effects) and idempotent.
GET /users/123GET /users?status=active&page=1GET /users/123/ordersCharacteristics:
POST is for creating new resources. It’s not idempotent (calling twice creates two resources).
POST /usersContent-Type: application/json
{ "name": "John Doe",}Characteristics:
Response:
HTTP/1.1 201 CreatedLocation: /users/456Content-Type: application/json
{ "id": 456, "name": "John Doe",}PUT replaces an entire resource. It’s idempotent (calling twice has same effect as once).
PUT /users/123Content-Type: application/json
{ "name": "Jane Doe", "status": "active"}Characteristics:
PATCH updates part of a resource. Should be idempotent if designed correctly.
PATCH /users/123Content-Type: application/json
{ "name": "Jane Doe"}Characteristics:
DELETE removes a resource. It’s idempotent (deleting twice = same as once).
DELETE /users/123Characteristics:
Status codes communicate the result of the request. Use them correctly!
| Code | Meaning | Use Case |
|---|---|---|
| 200 OK | Request succeeded | GET, PUT, PATCH |
| 201 Created | Resource created | POST (with Location header) |
| 204 No Content | Success, no body | DELETE, PUT (sometimes) |
| Code | Meaning | Use Case |
|---|---|---|
| 400 Bad Request | Invalid request | Malformed JSON, missing fields |
| 401 Unauthorized | Not authenticated | Missing/invalid token |
| 403 Forbidden | Not authorized | Valid token, but no permission |
| 404 Not Found | Resource doesn’t exist | Invalid ID, wrong URL |
| 409 Conflict | Resource conflict | Duplicate email, version conflict |
| 429 Too Many Requests | Rate limited | Too many requests |
| Code | Meaning | Use Case |
|---|---|---|
| 500 Internal Server Error | Server error | Unexpected exception |
| 502 Bad Gateway | Upstream error | Downstream service failed |
| 503 Service Unavailable | Service down | Maintenance, overloaded |
Do:
Don’t:
Versioning allows you to evolve your API without breaking existing clients.
Version in the URL path:
/api/v1/users/api/v2/usersPros:
Cons:
Version in HTTP headers:
GET /usersAccept: application/vnd.api+json;version=1Pros:
Cons:
Version as query parameter:
/api/users?version=1Pros:
Cons:
Bad:
POST /users/123/deleteGET /users/create?name=JohnPOST /users/123/updateWhy it’s bad: Uses verbs in URLs and wrong HTTP methods. Actions should be expressed through HTTP methods, not URL paths.
Good:
DELETE /users/123POST /users (with body)PUT /users/123 (with body)Why it’s good: Uses standard HTTP methods correctly. DELETE for deletion, POST for creation, PUT for updates.
Bad:
/getUsers/createUser/updateUser/deleteUserWhy it’s bad: URLs contain verbs, violating REST principles. The HTTP method already indicates the action.
Good:
GET /usersPOST /usersPUT /users/123DELETE /users/123Why it’s good: URLs contain only nouns (resources). Actions are expressed through HTTP methods.
Bad:
/user/orderWhy it’s bad: Singular nouns for collections are inconsistent and confusing. Is /user a single user or collection?
Good:
/users/ordersWhy it’s good: Plural nouns clearly indicate collections. Individual resources are identified by ID: /users/123.
Bad:
/orders?userId=123Why it’s bad: Uses query parameters to express relationships. Less intuitive and doesn’t show resource hierarchy.
Good:
/users/123/ordersWhy it’s good: Hierarchical URL clearly shows that orders belong to a user. More intuitive and RESTful.
Bad:
/users/customers/clientsWhy it’s bad: Inconsistent naming for the same concept. Confusing for API consumers who must remember different terms.
Good:
/users (consistent across API)Why it’s good: Consistent naming throughout the API. Once developers learn the pattern, they can predict other endpoints.
Bad:
// Always returns 200, even for errors{ "success": false, "error": "User not found"}Why it’s bad: Always returning 200 makes it impossible to use HTTP status codes for error handling. Clients must parse response body to detect errors.
Good:
HTTP/1.1 404 Not FoundContent-Type: application/json
{ "error": "User not found", "code": "USER_NOT_FOUND"}Why it’s good: Uses appropriate HTTP status code (404) for not found. Clients can handle errors based on status codes. Response body provides additional context.
Bad:
GET /users // Returns 10,000 usersWhy it’s bad: Returns all resources at once, causing performance issues, high memory usage, and slow response times.
Good:
GET /users?page=1&limit=20GET /users?offset=0&limit=20GET /users?cursor=abc123&limit=20Why it’s good: Pagination limits response size, improves performance, and reduces memory usage. Supports different pagination strategies (page-based, offset-based, cursor-based).
Response:
{ "data": [...], "pagination": { "page": 1, "limit": 20, "total": 1000, "hasNext": true }}GET /users?status=active&role=admin&sort=name&order=ascGET /users?search=john&limit=10Include links to related resources:
{ "id": 123, "name": "John Doe", "links": { "self": "/users/123", "orders": "/users/123/orders", "profile": "/users/123/profile" }}At the code level, REST APIs translate to controllers, services, and DTOs.
Make POST requests idempotent using idempotency keys:
POST /ordersIdempotency-Key: abc123-xyz789Content-Type: application/json
{ "productId": 456, "quantity": 2}Server behavior:
Support multiple formats:
GET /users/123Accept: application/json
GET /users/123Accept: application/xmlLet clients choose fields:
GET /users/123?fields=id,name,emailResources are Nouns
Use nouns for resources, HTTP methods for actions. /users with GET, not /getUsers.
Proper Status Codes
Use appropriate status codes. 201 for creation, 404 for not found, 400 for bad requests.
Idempotency Matters
GET, PUT, DELETE are idempotent. Design POST/PATCH to be idempotent when possible.
Layered Architecture
Controllers handle HTTP, Services handle business logic, Repositories handle data access.