v2.0 RESTful JSON
🔍

API Design Audit — User Management System

A developer submitted four REST API endpoints for a user management system. This document audits every design violation, explains the REST best practices being broken, and presents a fully corrected API reference with proper HTTP methods, resource-oriented URLs, consistent naming conventions, and accurate HTTP status codes.

🚨

Design Problems Identified

8 violations found across 4 endpoints

1
Verb in URL — Actions Should Be HTTP Methods
GET /getUsers

REST URLs must identify resources (nouns), not actions (verbs). The word get is redundant — the HTTP method GET already communicates the intent. Including verbs like getUsers, createUser, or deleteUser in the path is an RPC-style anti-pattern.

✅ Fix: GET /users
2
Wrong HTTP Method for Deletion
POST /user/delete/5

POST is semantically incorrect for deletion. HTTP defines DELETE specifically for removing resources. Using POST for destructive operations hides intent, breaks idempotency expectations, and prevents HTTP caches/proxies from behaving correctly.

✅ Fix: DELETE /users/5
3
Verb in URL (Again) + Wrong Method for Creation
GET /createUser?name=Jo

GET requests must be safe and idempotent — they must not cause side effects. Creating a resource is a side effect. Using GET to create data means web crawlers, browser prefetchers, and caches could accidentally create users. Additionally, createUser is a verb in the URL.

✅ Fix: POST /users with JSON body
4
Sensitive Data Exposed in Query String
GET /createUser?name=Jo

Passing user data (name, email, password) as query parameters exposes it in server logs, browser history, referrer headers, and network monitoring tools. Resource creation payloads must travel in the request body with Content-Type: application/json.

⚠️ Fix: Move data to JSON request body
5
Inconsistent Pluralization of Resource Names
POST /user/delete/5 vs GET /getUsers

The endpoints mix singular (/user/) and plural (/users) resource names. REST convention mandates plural nouns for collection endpoints (/users) for consistency and predictability. A single user is then /users/{id}.

✅ Fix: Always use /users and /users/{'{id}'}
6
Verb in URL — "delete" Embedded in Path
POST /user/delete/5

The path segment /delete/ is an action verb embedded in the URL. The HTTP method DELETE already expresses this intent. The URL should only identify the resource: /users/5. The method tells the server what to do with it.

✅ Fix: DELETE /users/5
7
Ambiguous Bulk Update — No Safety Guardrails
PUT /updateAllUsers

PUT /updateAllUsers has two issues: a verb in the URL, and PUT implies a complete replacement of a single resource. Bulk updates should use PATCH /users (partial update on the collection) with filtering/body constraints, and must require explicit confirmation to avoid accidental mass data changes.

⚠️ Fix: PATCH /users with request body filters
8
camelCase in URLs — Should Use kebab-case
GET /getUsers, GET /createUser, PUT /updateAllUsers

URL paths should use lowercase kebab-case (e.g., /user-profiles), not camelCase. URLs are case-sensitive on most servers; camelCase leads to inconsistency and hard-to-type paths. RFC 3986 and industry convention both recommend lowercase with hyphens as separators.

✅ Fix: Use lowercase: /users, /users/{'{id}'}
⚖️

Before & After Comparison

Original vs corrected endpoint mapping

❌ Original (Broken) ✅ Corrected (REST) Issues Fixed
GET /getUsers GET /users Verb removed Plural noun
POST /user/delete/5 DELETE /users/5 Correct method Verb removed Plural noun
GET /createUser?name=Jo POST /users Correct method Verb removed Body payload
PUT /updateAllUsers PATCH /users Correct method Verb removed Bulk PATCH

Corrected API Endpoints

Full reference with parameters, request bodies, and response examples

GET /users List all users (with pagination & filtering)

Returns a paginated collection of user objects. Supports filtering by status and sorting. This replaces the original broken GET /getUsers.

Query Parameters

NameTypeRequiredDescription
page integer optional Page number (default: 1)
limit integer optional Items per page (default: 20)
status string optional Filter: active | inactive

Response Status Codes

200 OK — Returns user array
400 Bad Request — Invalid query params
401 Unauthorized — Missing/invalid token
403 Forbidden — Insufficient permissions
500 Internal Server Error
Example Request
GET /users?page=1&limit=20&status=active
Authorization: Bearer <token>
Accept: application/json
200 OK — Response Body
{
  "status": "success",
  "data": {
    "users": [
      {
        "id": 1,
        "name": "Alice Johnson",
        "email": "[email protected]",
        "status": "active",
        "created_at": "2024-01-15T10:30:00Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 142,
      "total_pages": 8
    }
  }
}
GET /users/{id} Retrieve a single user by ID

Returns a single user resource identified by their unique integer ID. This endpoint is safe and idempotent.

Path Parameters

NameTypeRequiredDescription
id integer required Unique user identifier

Response Status Codes

200 OK — User object returned
404 Not Found — User ID doesn't exist
401 Unauthorized
200 OK — Response Body
{
  "status": "success",
  "data": {
    "user": {
      "id": 5,
      "name": "Bob Smith",
      "email": "[email protected]",
      "status": "active",
      "role": "editor",
      "created_at": "2024-02-20T08:00:00Z",
      "updated_at": "2024-06-01T14:22:00Z"
    }
  }
}
POST /users Create a new user — replaces GET /createUser?name=Jo

Creates a new user resource. Data is sent in the JSON request body — never in query parameters. Returns 201 Created with a Location header pointing to the new resource. Replaces the broken GET /createUser?name=Jo.

Request Body Fields

FieldTypeRequiredDescription
name string required Full name (min 2 chars)
email string required Valid email address
password string required Min 8 chars
role string optional viewer | editor | admin

Response Status Codes

201 Created — New user resource created
400 Bad Request — Validation failed
409 Conflict — Email already exists
422 Unprocessable Entity — Semantic errors
Request Body (application/json)
{
  "name":     "Jo Williams",
  "email":    "[email protected]",
  "password": "Secur3P@ss!",
  "role":     "editor"
}
201 Created — Response
// Headers
Location: /users/143
Content-Type: application/json

// Body
{
  "status": "success",
  "message": "User created successfully",
  "data": {
    "user": {
      "id": 143,
      "name": "Jo Williams",
      "email": "[email protected]",
      "role": "editor",
      "created_at": "2024-07-10T12:00:00Z"
    }
  }
}
PUT /users/{id} Fully replace a single user resource

Complete replacement of a user resource. All fields must be provided. Missing fields will be set to null/default. Use PATCH for partial updates. This is idempotent — repeated calls produce the same result.

Path Parameters

NameTypeRequired
id integer required

Response Status Codes

200 OK — User fully updated
400 Bad Request — Missing required fields
404 Not Found — User doesn't exist
Request Body — All Fields Required
{
  "name":   "Jo Williams",
  "email":  "[email protected]",
  "role":   "admin",
  "status": "active"
}
PATCH /users/{id} Partially update a single user

Updates only the fields provided in the request body. Unspecified fields remain unchanged. Preferred over PUT for most update operations.

Request Body — Only Changed Fields
{
  "status": "inactive"
  // Only this field will be updated
}

Response Status Codes

200 OK — Fields updated successfully
404 Not Found
422 Unprocessable — Invalid field values
PATCH /users Bulk partial update — replaces PUT /updateAllUsers

Performs a partial update on multiple users matching a filter. Requires explicit confirmation via the X-Confirm-Bulk: true header to prevent accidental mass changes. Replaces the broken PUT /updateAllUsers.

Required Headers

HeaderValuePurpose
X-Confirm-Bulk true Safety guard for bulk ops

Response Status Codes

200 OK — Returns count of updated records
400 Bad Request — Missing confirmation header
403 Forbidden — Admin role required
Request — Bulk Update Active Users
PATCH /users
X-Confirm-Bulk: true
Content-Type: application/json

{
  "filter": { "status": "active" },
  "update": { "role": "viewer" }
}
200 OK — Response
{
  "status": "success",
  "data": {
    "matched": 89,
    "updated": 89
  },
  "message":