| name | api-design-principles |
| description | 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. |
| layer | 2 |
| tech_stack | ["agnostic"] |
| topics | ["rest-api","versioning","pagination","http-methods","status-codes","api-design"] |
| depends_on | [] |
| complements | ["technical-design-patterns","abp-api-implementation"] |
| keywords | ["REST","API","Endpoint","GET","POST","PUT","DELETE","OpenAPI","Swagger","HTTP"] |
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:
{
"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:
{
"id": "patient-123",
"name": "John Doe",
"doctor": {
"id": "doctor-456",
"name": "Dr. Smith"
}
}
{
"id": "patient-123",
"name": "John Doe",
"doctorId": "doctor-456"
}
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
Request/Response
Security
Documentation
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