REST API Design Best Practices: Resources, Methods and Status Codes
Practical rules for designing clean REST APIs: naming resources, using HTTP methods and status codes correctly, handling errors, pagination and versioning, and securing endpoints.
SmartCampus Buddy TeamSeptember 7, 20269 min read
A REST API is how a front end, a mobile app or another service talks to your backend over HTTP. A well-designed one is predictable, so people can guess how it works. The guidelines below apply whichever language or framework you use, including Spring Boot and Node.js.
Model resources with nouns
Design URLs around things (resources), not actions. Use plural nouns and let the HTTP method describe the action.
GET /bookslists books.GET /books/42fetches one book.POST /bookscreates a book.PUT /books/42replaces a book, andPATCH /books/42changes part of it.DELETE /books/42removes it.
Avoid verbs in paths such as /getBooks or /deleteBook. For related data, nest sensibly, for example /books/42/reviews, and avoid going deeper than two levels.
Use HTTP methods correctly
- GET reads data and must not change anything. It is safe to repeat and cache.
- POST creates a resource or triggers a non-repeatable action.
- PUT and DELETE are idempotent, meaning repeating the same request leaves the same result. Sending the same PUT twice should not create two records.
- PATCH applies a partial update.
Return meaningful status codes
The status code tells the client what happened before it reads the body.
- 200 OK: success with a body.
- 201 Created: a resource was created, ideally with a
Locationheader for the new URL. - 204 No Content: success with nothing to return, common for DELETE.
- 400 Bad Request: the request is malformed or fails validation.
- 401 Unauthorized: the caller is not authenticated.
- 403 Forbidden: authenticated but not allowed.
- 404 Not Found: the resource does not exist.
- 409 Conflict: the request conflicts with current state, for example a duplicate email.
- 500 Internal Server Error: something failed on the server.
Do not return 200 with an error message in the body. Clients and monitoring tools rely on the status code.
Make errors consistent
Pick one error format and use it everywhere.
{
"status": 400,
"error": "Validation failed",
"details": [{ "field": "email", "message": "must be a valid email address" }]
}Give enough detail to fix the request, but never leak stack traces, SQL or internal paths.
Paginate, filter and sort
Never return an unbounded list. Support parameters such as ?page=2&size=20 or a cursor, and allow filtering and sorting through query parameters like ?status=active&sort=createdAt. Backed by a database, these queries depend on good indexes, see SQL indexes explained.
Validate input on the server
Never trust the client. Check types, lengths and required fields for every request, and use parameterised queries so user input can never change the meaning of a SQL statement. The SQL Basics quiz includes a question on injection.
Secure your API
- Use HTTPS everywhere.
- Authenticate requests, for example with tokens, and check permissions for every resource, not only at login.
- Store passwords only as salted hashes made with a slow algorithm such as bcrypt or Argon2, never as plain text.
- Keep secrets out of source code and URLs.
- Limit request rates to reduce abuse.
Plan for change
Once clients depend on your API, changes can break them. Prefer adding fields over renaming them, and introduce a version, such as /v1/books or a header, before making incompatible changes.
Document it
Provide a description of every endpoint, its parameters, request and response examples and error cases. A machine-readable specification, such as OpenAPI, lets tools generate documentation and test requests.
Key takeaways
- Name resources with plural nouns and use methods for the actions.
- Use accurate status codes and one consistent error format.
- Paginate lists and validate everything on the server.
- Secure the API and version it before breaking changes.
- Put it all together with the backend developer roadmap.