# REST API Standards

> **Scope:** universal
> **Layer:** 0 (always load)
> **Keywords:** rest, api, endpoint, controller, http, restful
> **Load When:** always

RESTful API design conventions and best practices

---

## HTTP Methods

| Method | Usage | Idempotent | Safe |
|--------|-------|------------|------|
| GET | Retrieve resource(s) | ✅ Yes | ✅ Yes |
| POST | Create new resource | ❌ No | ❌ No |
| PUT | Replace entire resource | ✅ Yes | ❌ No |
| PATCH | Partial update | ❌ No | ❌ No |
| DELETE | Remove resource | ✅ Yes | ❌ No |

---

## URL Conventions

### Resource Naming

```
✅ GOOD
/api/users
/api/users/123
/api/users/123/orders
/api/orders/456/items

❌ BAD
/api/getUsers
/api/user
/api/User
/api/users/getAllOrders
```

### Rules

- Use **plural nouns** for collections (`/users`, not `/user`)
- Use **lowercase** (`/users`, not `/Users`)
- Use **hyphens** for multi-word resources (`/order-items`, not `/orderItems`)
- Use **forward slashes** for hierarchy (`/users/123/orders`)
- **No trailing slash** (`/users`, not `/users/`)
- **No verbs** in URLs (`/users`, not `/getUsers`)

---

## Endpoint Patterns

### Collection Operations

```http
GET    /api/users              # List all users
POST   /api/users              # Create new user
GET    /api/users/123          # Get user by ID
PUT    /api/users/123          # Replace user
PATCH  /api/users/123          # Update user
DELETE /api/users/123          # Delete user
```

### Nested Resources

```http
GET    /api/users/123/orders           # Get user's orders
POST   /api/users/123/orders           # Create order for user
GET    /api/users/123/orders/456       # Get specific order
DELETE /api/users/123/orders/456       # Delete user's order
```

### Actions (Non-CRUD)

```http
POST   /api/users/123/activate         # Activate user
POST   /api/orders/456/cancel          # Cancel order
POST   /api/orders/456/refund          # Refund order
```

---

## HTTP Status Codes

### Success (2xx)

| Code | Meaning | Usage |
|------|---------|-------|
| 200 OK | Success | GET, PUT, PATCH (with body) |
| 201 Created | Resource created | POST |
| 204 No Content | Success, no body | DELETE, PUT, PATCH (no body) |

### Client Error (4xx)

| Code | Meaning | Usage |
|------|---------|-------|
| 400 Bad Request | Invalid input | Validation errors |
| 401 Unauthorized | Not authenticated | Missing/invalid token |
| 403 Forbidden | Not authorized | Insufficient permissions |
| 404 Not Found | Resource not found | Invalid ID |
| 409 Conflict | Conflict | Duplicate email, version conflict |
| 422 Unprocessable Entity | Validation failed | Business rule violation |

### Server Error (5xx)

| Code | Meaning | Usage |
|------|---------|-------|
| 500 Internal Server Error | Unexpected error | Unhandled exceptions |
| 503 Service Unavailable | Service down | Maintenance, overload |

---

## Request/Response Examples

### GET (List)

**Request:**

```http
GET /api/users?page=1&limit=20&sort=email&filter=active
```

**Response:**

```json
{
  "data": [
    {
      "id": 1,
      "email": "user@example.com",
      "name": "John Doe",
      "createdAt": "2026-02-16T10:00:00Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 45,
    "totalPages": 3
  }
}
```

### GET (Single)

**Request:**

```http
GET /api/users/123
```

**Response:**

```json
{
  "id": 123,
  "email": "user@example.com",
  "name": "John Doe",
  "createdAt": "2026-02-16T10:00:00Z"
}
```

### POST (Create)

**Request:**

```http
POST /api/users
Content-Type: application/json

{
  "email": "newuser@example.com",
  "name": "Jane Smith",
  "password": "SecurePass123!"
}
```

**Response:**

```http
HTTP/1.1 201 Created
Location: /api/users/124

{
  "id": 124,
  "email": "newuser@example.com",
  "name": "Jane Smith",
  "createdAt": "2026-02-16T11:00:00Z"
}
```

### PUT (Replace)

**Request:**

```http
PUT /api/users/123
Content-Type: application/json

{
  "email": "updated@example.com",
  "name": "John Updated"
}
```

**Response:**

```http
HTTP/1.1 200 OK

{
  "id": 123,
  "email": "updated@example.com",
  "name": "John Updated",
  "updatedAt": "2026-02-16T11:30:00Z"
}
```

### DELETE

**Request:**

```http
DELETE /api/users/123
```

**Response:**

```http
HTTP/1.1 204 No Content
```

---

## Error Responses

### Standard Error Format

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed for one or more fields",
    "details": [
      {
        "field": "email",
        "message": "Email is required"
      },
      {
        "field": "password",
        "message": "Password must be at least 8 characters"
      }
    ]
  }
}
```

### Error Codes

```json
// 400 Bad Request
{
  "error": {
    "code": "INVALID_INPUT",
    "message": "Invalid request body"
  }
}

// 401 Unauthorized
{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Authentication required"
  }
}

// 403 Forbidden
{
  "error": {
    "code": "FORBIDDEN",
    "message": "Insufficient permissions to access this resource"
  }
}

// 404 Not Found
{
  "error": {
    "code": "NOT_FOUND",
    "message": "User with ID 123 not found"
  }
}

// 409 Conflict
{
  "error": {
    "code": "CONFLICT",
    "message": "Email already exists"
  }
}

// 500 Internal Server Error
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred"
  }
}
```

---

## Pagination

### Query Parameters

```http
GET /api/users?page=2&limit=20
```

### Response Format

```json
{
  "data": [...],
  "meta": {
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8
  },
  "links": {
    "first": "/api/users?page=1&limit=20",
    "prev": "/api/users?page=1&limit=20",
    "self": "/api/users?page=2&limit=20",
    "next": "/api/users?page=3&limit=20",
    "last": "/api/users?page=8&limit=20"
  }
}
```

---

## Filtering and Sorting

### Query Parameters

```http
# Filter by status
GET /api/orders?status=pending

# Filter by date range
GET /api/orders?startDate=2026-01-01&endDate=2026-02-01

# Sort ascending
GET /api/users?sort=email

# Sort descending
GET /api/users?sort=-createdAt

# Multiple filters
GET /api/orders?status=pending&customer=123&sort=-createdAt
```

---

## Versioning

### URL Versioning (Recommended)

```http
GET /api/v1/users
GET /api/v2/users
```

### Header Versioning

```http
GET /api/users
Accept: application/vnd.api.v1+json
```

---

## Best Practices

### DO

✅ **Use plural nouns**

```http
✅ GET /api/users
❌ GET /api/user
```

✅ **Return appropriate status codes**

```csharp
// ✅ CORRECT
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserRequest request)
{
    var user = await _userService.CreateAsync(request);
    return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
```

✅ **Use DTOs (not entities)**

```csharp
// ✅ CORRECT
public record UserResponse(int Id, string Email, string Name);

[HttpGet("{id}")]
public async Task<ActionResult<UserResponse>> GetUser(int id)
{
    var user = await _userService.GetByIdAsync(id);
    return Ok(new UserResponse(user.Id, user.Email, user.Name));
}
```

✅ **Validate input**

```csharp
public record CreateUserRequest
{
    [Required]
    [EmailAddress]
    public string Email { get; init; }

    [Required]
    [MinLength(8)]
    public string Password { get; init; }
}
```

### DON'T

❌ **Don't use verbs in URLs**

```http
❌ POST /api/createUser
✅ POST /api/users
```

❌ **Don't expose internal IDs**

```csharp
// ❌ WRONG (exposes database ID)
public class User
{
    public int Id { get; set; } // Internal DB ID
}

// ✅ CORRECT (use public UUID)
public class UserResponse
{
    public Guid PublicId { get; init; }
}
```

❌ **Don't return entities directly**

```csharp
// ❌ WRONG
[HttpGet]
public async Task<List<User>> GetUsers()
{
    return await _context.Users.ToListAsync(); // Exposes all properties!
}

// ✅ CORRECT
[HttpGet]
public async Task<List<UserResponse>> GetUsers()
{
    var users = await _context.Users.ToListAsync();
    return users.Select(u => new UserResponse(u.Id, u.Email, u.Name)).ToList();
}
```

---

## Related Standards

- [Error Handling](./error-handling.md)
- [Validation](./validation.md)
- [Minimal API](./ minimal-api.md)

---

*MORPH-SPEC by Polymorphism Tech*
