🔍 REST API Design Review

Identifying design problems in the original endpoints and presenting corrected versions following REST best practices.

4
Endpoints Reviewed
12
Issues Found
4
Corrected Endpoints
10
Status Codes Defined
6
Best Practices
⚠️
Design Problems in Original Endpoints
GET /getUsers Verb in URL · Non-plural resource
Naming Verb in URL: REST URLs should identify resources, not actions. The HTTP method (GET) already communicates the action — embedding "get" in the path is redundant and breaks the resource-oriented model.
Resource Missing plural noun: Collections should use plural nouns. /getUsers mixes a verb with a noun; the correct form is /users — clean, predictable, and consistent with REST conventions.
Design No filtering / pagination support: A collection endpoint should accept query parameters such as ?page=1&limit=20&sort=name to avoid returning unbounded result sets.
❌ Before
GET /getUsers
✅ After
GET /users?page=1&limit=20
POST /user/delete/5 Wrong HTTP method · Verb in URL · Singular resource
Method POST used for deletion: POST is semantically for creating new resources. Deletions must use the DELETE method — this is a core HTTP specification rule, not a convention. Misusing POST breaks caching, idempotency guarantees, and API tooling.
Naming Verb "delete" embedded in path: The HTTP method already declares the intent. Adding "delete" to the URL duplicates semantics and makes the API harder to reason about programmatically.
Resource Singular resource name: Use /users (plural) consistently across all endpoints for the same resource. Mixing /user and /users creates confusion.
Design Idempotency broken: DELETE should be idempotent — calling it multiple times should yield the same result (resource gone). POST is not idempotent, which misleads clients and intermediaries.
❌ Before
POST /user/delete/5
✅ After
DELETE /users/5
GET /createUser?name=Jo Wrong HTTP method · Verb in URL · Data in query string · Short value
Method GET used for resource creation: GET must be safe (no side effects) and idempotent. Creating a resource is a side effect. Use POST to create, which correctly signals a non-idempotent, state-changing operation.
Security Sensitive data in query string: Query parameters appear in server logs, browser history, and HTTP Referer headers. User data (names, emails, passwords) must travel in the request body, not the URL.
Naming Verb "create" in URL: Same issue as above — POST to /users already implies creation. The verb is entirely redundant.
Design Incomplete / unvalidated data: name=Jo suggests no server-side validation minimum length, no required fields like email, and no structured payload. The request body should carry a full, validated JSON object.
❌ Before
GET /createUser?name=Jo
✅ After
POST /users Body: {"name":"Jo","email":"..."}
PUT /updateAllUsers Verb in URL · Dangerous bulk operation · Wrong method semantics
Method PUT semantics misapplied: PUT replaces a single, identified resource entirely. Applying it to a collection without an ID is semantically incorrect. Bulk partial updates should use PATCH on the collection, or be broken into individual PATCH requests.
Naming Verb "update" in URL: The HTTP method communicates the action. /updateAllUsers should simply be /users — the PATCH method signals a partial update.
Design Dangerous bulk operation without safeguards: Updating all users in one shot is a destructive operation. Best practice is to require explicit IDs, use batch endpoints like PATCH /users with a body array, and enforce authorization checks per record.
Design No resource identifier: REST endpoints for mutations on a specific resource must include the resource ID in the path, e.g. /users/{id}. Omitting it makes the scope of the operation dangerously ambiguous.
❌ Before
PUT /updateAllUsers
✅ After
PATCH /users/{id} — or for bulk — PATCH /users (body: array)
Corrected Endpoints with Full Specification
GET /users
Retrieve a paginated list of users
Query Parameters
page (integer, default 1) — Page number limit (integer, default 20) — Items per page (max 100) sort (string) — Field to sort by, e.g. "name" order (asc | desc) — Sort direction search (string) — Optional name/email filter
Example Response — 200 OK
{ "data": [ { "id": 1, "name": "Alice", "email": "[email protected]" }, { "id": 2, "name": "Bob", "email": "[email protected]" } ], "meta": { "page": 1, "limit": 20, "totalItems": 84, "totalPages": 5 } }
Status Codes
200 OK
400 Bad Request (invalid params)
401 Unauthorized
403 Forbidden
500 Internal Server Error
POST /users
Create a new user
Request Body (application/json)
{ "name": "Jo Smith", // required, min 2 chars "email": "[email protected]", // required, valid email "password": "••••••••", // required, min 8 chars "role": "user" // optional, default "user" }
Example Response — 201 Created
{ "id": 42, "name": "Jo Smith", "email": "[email protected]", "role": "user", "createdAt": "2024-01-15T10:30:00Z" } // Location header: /users/42
Status Codes
201 Created
400 Validation Error
401 Unauthorized
409 Conflict (email exists)
500 Internal Server Error
PATCH /users/{id}
Partially update a specific user
Path Parameters
id (integer, required) — Unique user identifier
Request Body — send only fields to update
{ "name": "Jo A. Smith", // optional "email": "[email protected]" // optional }
Example Response — 200 OK
{ "id": 5, "name": "Jo A. Smith", "email": "[email protected]", "updatedAt": "2024-01-15T11:00:00Z" }
Status Codes
200 OK
400 Validation Error
401 Unauthorized
403 Forbidden
404 User Not Found
500 Internal Server Error
DELETE /users/{id}
Delete a specific user
Path Parameters
id (integer, required) — Unique user identifier
Behavior
• Idempotent — repeated calls return the same result • Returns 204 No Content on success (no body) • Returns 404 if the user does not exist • Soft-delete recommended: set deletedAt timestamp rather than hard-removing the record
Status Codes
204 No Content
401 Unauthorized
403 Forbidden
404 User Not Found
500 Internal Server Error
📖
REST API Best Practices
🔤

Use Nouns, Not Verbs

URLs identify resources. HTTP methods (GET, POST, PUT, PATCH, DELETE) express the action. Never embed verbs like "get", "create", "delete", or "update" in your paths.

✅ GET /users/5 ❌ GET /getUser?id=5
📦

Plural Resource Names

Use plural nouns consistently for collections. This keeps the API predictable — /users always refers to the user collection, and /users/5 to a specific user.

✅ GET /users ✅ GET /users/5 ❌ GET /user/5
🔀

Correct HTTP Methods

Each method has a defined semantic. Respect them: GET (safe, idempotent read), POST (create), PUT (full replace), PATCH (partial update), DELETE (remove).

✅ DELETE /users/5 ❌ POST /users/delete/5
🔢

Meaningful Status Codes

Return the most specific status code: 201 for creation, 204 for successful deletion, 400 for validation errors, 404 for missing resources, 409 for conflicts.

✅ 201 Created (POST /users) ✅ 204 No Content (DELETE /users/5) ❌ 200 OK for everything
🔒

Never Expose Data in URLs

Query strings appear in logs, browser history, and Referer headers. Sensitive data (passwords, tokens, PII) must always travel in the request body over HTTPS.

✅ POST /users { body: {...} } ❌ GET /createUser?password=abc
📄

Versioning & Pagination

Version your API from day one to avoid breaking changes. Always paginate collection endpoints — returning unbounded datasets is a reliability and performance risk.

✅ GET /v1/users?page=1&limit=20 ✅ Location: /v1/users/42