# API Design Principles

> Master REST API design principles to build intuitive, scalable, and maintainable APIs. Use when designing new APIs, reviewing API specifications, or establishing API design standards.

- Skill: `majiayu000/api-design-principles` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/api-design-principles`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/api-design-principles/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/api-design-principles

---


# API Design Principles

Master REST API design principles to build intuitive, scalable, and maintainable APIs. This skill focuses on **design theory** - for implementation patterns, see `abp-api-implementation` or `abp-service-patterns`.

## When to Use This Skill

- Designing new REST API contracts
- Reviewing API specifications before implementation
- Establishing API design standards for your team
- Planning API versioning and evolution strategy
- Creating developer-friendly API documentation

## Audience

- **Backend Architects** - API contract design
- **Tech Leads** - Standards and review
- **Business Analysts** - Understanding API capabilities

> **For Implementation**: Use `abp-service-patterns` for AppService code, `api-response-patterns` for response wrappers, `fluentvalidation-patterns` for validation.

---

## Core Principles

### 1. Resource-Oriented Design

APIs expose **resources** (nouns), not **actions** (verbs).

| Concept | Good | Bad |
|---------|------|-----|
| Resource naming | `/patients`, `/appointments` | `/getPatients`, `/createAppointment` |
| Actions via HTTP methods | `POST /patients` | `POST /createPatient` |
| Plural for collections | `/patients` | `/patient` |
| Consistent casing | `kebab-case` or `camelCase` | Mixed styles |

**Resource Hierarchy**:
```
/api/v1/patients                    # Collection
/api/v1/patients/{id}               # Single resource
/api/v1/patients/{id}/appointments  # Nested collection
/api/v1/appointments/{id}           # Direct access to nested resource
```

**Avoid Deep Nesting** (max 2 levels):
```
# Good - Shallow
GET /api/v1/patients/{id}/appointments
GET /api/v1/appointments/{id}

# Bad - Too deep
GET /api/v1/clinics/{id}/doctors/{id}/patients/{id}/appointments/{id}
```

### 2. HTTP Methods Semantics

| Method | Purpose | Idempotent | Safe | Request Body |
|--------|---------|------------|------|--------------|
| `GET` | Retrieve resource(s) | Yes | Yes | No |
| `POST` | Create resource | No | No | Yes |
| `PUT` | Replace entire resource | Yes | No | Yes |
| `PATCH` | Partial update | Yes* | No | Yes |
| `DELETE` | Remove resource | Yes | No | No |

**Idempotent**: Multiple identical requests produce same result.
**Safe**: Does not modify server state.

### 3. HTTP Status Codes

**Success (2xx)**:
| Code | Meaning | Use When |
|------|---------|----------|
| `200 OK` | Success | GET, PUT, PATCH succeeded |
| `201 Created` | Resource created | POST succeeded |
| `204 No Content` | Success, no body | DELETE succeeded |

**Client Errors (4xx)**:
| Code | Meaning | Use When |
|------|---------|----------|
| `400 Bad Request` | Malformed request | Invalid JSON, missing required headers |
| `401 Unauthorized` | Not authenticated | Missing or invalid token |
| `403 Forbidden` | Not authorized | Valid token, insufficient permissions |
| `404 Not Found` | Resource doesn't exist | ID not found |
| `409 Conflict` | State conflict | Duplicate email, version mismatch |
| `422 Unprocessable Entity` | Validation failed | Business rule violations |
| `429 Too Many Requests` | Rate limited | Exceeded request quota |

**Server Errors (5xx)**:
| Code | Meaning | Use When |
|------|---------|----------|
| `500 Internal Server Error` | Unexpected error | Unhandled exception |
| `503 Service Unavailable` | Temporarily down | Maintenance, overload |

---

## Design Patterns

### Pattern 1: Pagination

**Always paginate collections** - Never return unbounded lists.

**Offset-Based** (simple, good for small datasets):
```
GET /api/v1/patients?page=2&pageSize=20

Response:
{
  "items": [...],
  "totalCount": 150,
  "pageNumber": 2,
  "pageSize": 20,
  "totalPages": 8
}
```

**Cursor-Based** (efficient for large datasets, real-time data):
```
GET /api/v1/patients?cursor=eyJpZCI6MTIzfQ&limit=20

Response:
{
  "items": [...],
  "nextCursor": "eyJpZCI6MTQzfQ",
  "hasMore": true
}
```

| Approach | Pros | Cons | Best For |
|----------|------|------|----------|
| Offset | Simple, supports jumping to page | Slow on large datasets, inconsistent with real-time data | Admin panels, reports |
| Cursor | Fast, consistent | Can't jump to arbitrary page | Infinite scroll, feeds |

### Pattern 2: Filtering and Sorting

**Query Parameters for Filtering**:
```
GET /api/v1/patients?status=active
GET /api/v1/patients?status=active&createdAfter=2025-01-01
GET /api/v1/patients?doctorId=abc-123
```

**Sorting**:
```
GET /api/v1/patients?sorting=name
GET /api/v1/patients?sorting=createdAt desc
GET /api/v1/patients?sorting=lastName,firstName
```

**Searching**:
```
GET /api/v1/patients?filter=john
GET /api/v1/patients?search=john doe
```

**Design Decisions**:
- Use `WhereIf` pattern - only apply filter if parameter provided
- Define allowed sort fields (security - don't expose internal fields)
- Set maximum page size (prevent abuse)

### Pattern 3: Error Response Design

**Consistent Structure**:
```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "One or more validation errors occurred.",
    "details": [
      {
        "field": "email",
        "message": "Invalid email format."
      },
      {
        "field": "dateOfBirth",
        "message": "Date of birth cannot be in the future."
      }
    ],
    "traceId": "00-abc123-def456-00"
  }
}
```

**Error Codes** (for client handling):
| Code | HTTP Status | Meaning |
|------|-------------|---------|
| `VALIDATION_ERROR` | 422 | Input validation failed |
| `NOT_FOUND` | 404 | Resource doesn't exist |
| `UNAUTHORIZED` | 401 | Authentication required |
| `FORBIDDEN` | 403 | Permission denied |
| `CONFLICT` | 409 | State conflict |
| `RATE_LIMITED` | 429 | Too many requests |

### Pattern 4: Versioning Strategy

**URL Versioning** (Recommended for ABP):
```
/api/v1/patients
/api/v2/patients
```

| Strategy | Example | Pros | Cons |
|----------|---------|------|------|
| URL Path | `/api/v1/` | Clear, easy routing | Multiple URLs |
| Header | `Api-Version: 1` | Clean URLs | Hidden, harder to test |
| Query Param | `?version=1` | Easy testing | Can be forgotten |

**Versioning Policy**:
- Major version for breaking changes
- Support N-1 version minimum
- Deprecation notice 6+ months before removal
- Document migration path

### Pattern 5: Resource Relationships

**Embedding vs Linking**:

```json
// Embedded (fewer requests, larger payload)
{
  "id": "patient-123",
  "name": "John Doe",
  "doctor": {
    "id": "doctor-456",
    "name": "Dr. Smith"
  }
}

// Linked (smaller payload, more requests)
{
  "id": "patient-123",
  "name": "John Doe",
  "doctorId": "doctor-456"
}

// Hybrid (with expand parameter)
GET /api/v1/patients/123?expand=doctor,appointments
```

**Decision Criteria**:
| Use Embedding | Use Linking |
|---------------|-------------|
| Related data always needed | Related data rarely needed |
| Few relationships | Many relationships |
| Related data is small | Related data is large |

---

## API Contract Checklist

### Resource Design
- [ ] Resources are nouns, not verbs
- [ ] Plural names for collections
- [ ] Consistent naming convention
- [ ] Max 2 levels of nesting
- [ ] All CRUD mapped to correct HTTP methods

### Request/Response
- [ ] All collections paginated
- [ ] Default and max page size defined
- [ ] Filter parameters documented
- [ ] Sorting parameters documented
- [ ] Consistent error response format

### Security
- [ ] Authentication method defined
- [ ] Authorization on all mutating endpoints
- [ ] Rate limiting configured
- [ ] Sensitive data not in URLs
- [ ] CORS configured

### Documentation
- [ ] OpenAPI/Swagger spec
- [ ] All endpoints documented
- [ ] Request/response examples
- [ ] Error responses documented

---

## Anti-Patterns to Avoid

| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| Verb endpoints | `POST /createPatient` | `POST /patients` |
| Ignoring HTTP methods | Using POST for everything | Use appropriate method |
| No pagination | Returning 10,000 items | Always paginate |
| Inconsistent errors | Different formats per endpoint | Standardize error structure |
| Exposing internals | Database columns in API | Design API contract separately |
| No versioning | Breaking changes break clients | Version from day one |
| Deep nesting | `/a/{id}/b/{id}/c/{id}/d` | Flatten, max 2 levels |

---

## Integration with Other Skills

| Need | Skill |
|------|-------|
| **AppService implementation** | `abp-service-patterns` |
| **Response wrappers** | `api-response-patterns` |
| **Input validation** | `fluentvalidation-patterns` |
| **Query optimization** | `linq-optimization-patterns` |
| **Technical design docs** | `technical-design-patterns` |

---

## References

- `references/rest-best-practices.md` - Detailed REST patterns
- `assets/api-design-checklist.md` - Pre-implementation checklist

