Back to skills
Api Design Patterns
REST and GraphQL API design patterns, OpenAPI/Swagger specifications, versioning strategies, and authentication patterns. Use when designing APIs, reviewing API contracts, evaluating API technologies, or implementing API endpoints. Covers contract-first design, resource modeling, error handling, pagination, and security.
127 stars
0 votes
0 copies
0 views
Added 12/19/2025
developmentgonodetestingapidatabasesecuritydocumentation
Works with
cursorcliapi
Install via CLI
$
openskills install rsmdt/the-startupFiles
SKILL.md
---
name: api-design-patterns
description: REST and GraphQL API design patterns, OpenAPI/Swagger specifications, versioning strategies, and authentication patterns. Use when designing APIs, reviewing API contracts, evaluating API technologies, or implementing API endpoints. Covers contract-first design, resource modeling, error handling, pagination, and security.
---
# API Design Patterns
A comprehensive skill for designing, documenting, and implementing APIs that developers love to use. Covers REST, GraphQL, and hybrid approaches with emphasis on consistency, discoverability, and maintainability.
## When to Use
- Designing new REST or GraphQL APIs from scratch
- Reviewing existing API contracts for consistency and best practices
- Evaluating API technologies and frameworks
- Implementing API versioning strategies
- Designing authentication and authorization flows
- Creating OpenAPI/Swagger specifications
- Building developer-friendly API documentation
## Core Principles
### 1. Contract-First Design
Define the API contract before implementation. This enables parallel development, clearer communication, and better documentation.
```
DESIGN SEQUENCE:
1. IDENTIFY use cases and consumer needs
2. MODEL resources and their relationships
3. DEFINE operations (CRUD + custom actions)
4. SPECIFY request/response schemas
5. DOCUMENT error scenarios
6. VALIDATE with consumers before implementing
```
### 2. Consistency Over Cleverness
APIs should be predictable. Developers should be able to guess how an endpoint works based on patterns established elsewhere in the API.
```
CONSISTENCY CHECKLIST:
- Naming conventions (plural nouns, kebab-case)
- Response envelope structure
- Error format across all endpoints
- Pagination approach
- Query parameter patterns
- Date/time formatting (ISO 8601)
```
### 3. Design for Evolution
APIs must evolve without breaking existing consumers. Plan for change from day one.
```
EVOLUTION STRATEGIES:
- Additive changes only (new fields, endpoints)
- Deprecation with sunset periods
- Version negotiation (headers, URL paths)
- Backward compatibility testing
```
## REST API Patterns
### Resource Modeling
Resources represent business entities. URLs should reflect the resource hierarchy.
```
GOOD:
GET /users # List users
POST /users # Create user
GET /users/{id} # Get user
PATCH /users/{id} # Partial update
DELETE /users/{id} # Delete user
GET /users/{id}/orders # User's orders (sub-resource)
AVOID:
GET /getUsers # Verbs in URLs
POST /createNewUser # Redundant verbs
GET /user-list # Inconsistent naming
POST /users/{id}/delete # Wrong HTTP method
```
### HTTP Method Semantics
| Method | Usage | Idempotent | Safe |
|--------|-------|------------|------|
| GET | Retrieve resource(s) | Yes | Yes |
| POST | Create resource, trigger action | No | No |
| PUT | Replace entire resource | Yes | No |
| PATCH | Partial update | Yes | No |
| DELETE | Remove resource | Yes | No |
| OPTIONS | CORS preflight, capability discovery | Yes | Yes |
### Status Code Selection
```
SUCCESS:
200 OK - Successful GET, PUT, PATCH, DELETE
201 Created - Successful POST (include Location header)
202 Accepted - Async operation started
204 No Content - Success with no response body
CLIENT ERRORS:
400 Bad Request - Malformed request, validation failure
401 Unauthorized - Missing or invalid authentication
403 Forbidden - Authenticated but not authorized
404 Not Found - Resource doesn't exist
409 Conflict - State conflict (duplicate, version mismatch)
422 Unprocessable- Semantically invalid (business rule violation)
429 Too Many - Rate limit exceeded
SERVER ERRORS:
500 Internal - Unexpected server error
502 Bad Gateway - Upstream service failure
503 Unavailable - Temporary overload or maintenance
504 Gateway Timeout - Upstream timeout
```
### Error Response Format
Standardize error responses across all endpoints:
```json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "Email must be a valid email address"
}
],
"requestId": "req_abc123",
"timestamp": "2025-01-15T10:30:00Z",
"documentation": "https://api.example.com/docs/errors#VALIDATION_ERROR"
}
}
```
### Pagination Patterns
#### Offset-Based (Simple, not for large datasets)
```
GET /users?offset=20&limit=10
Response:
{
"data": [...],
"pagination": {
"total": 150,
"offset": 20,
"limit": 10,
"hasMore": true
}
}
```
#### Cursor-Based (Recommended for large datasets)
```
GET /users?cursor=eyJpZCI6MTAwfQ&limit=10
Response:
{
"data": [...],
"pagination": {
"nextCursor": "eyJpZCI6MTEwfQ",
"prevCursor": "eyJpZCI6OTB9",
"hasMore": true
}
}
```
### Filtering and Sorting
```
FILTERING:
GET /users?status=active # Exact match
GET /users?created_after=2025-01-01 # Date range
GET /users?role=admin,moderator # Multiple values
GET /users?search=john # Full-text search
SORTING:
GET /users?sort=created_at # Ascending (default)
GET /users?sort=-created_at # Descending (prefix -)
GET /users?sort=status,-created_at # Multiple fields
FIELD SELECTION:
GET /users?fields=id,name,email # Sparse fieldsets
GET /users?expand=organization # Include related
```
## GraphQL Patterns
### Schema Design Principles
```graphql
# Use clear, descriptive type names
type User {
id: ID!
email: String!
displayName: String!
createdAt: DateTime!
# Relationships with clear naming
organization: Organization
orders(first: Int, after: String): OrderConnection!
}
# Use connections for paginated lists
type OrderConnection {
edges: [OrderEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
type OrderEdge {
node: Order!
cursor: String!
}
type PageInfo {
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
endCursor: String
}
```
### Query Design
```graphql
type Query {
# Single resource by ID
user(id: ID!): User
# List with filtering and pagination
users(
filter: UserFilter
first: Int
after: String
orderBy: UserOrderBy
): UserConnection!
# Viewer pattern for current user
viewer: User
}
input UserFilter {
status: UserStatus
organizationId: ID
searchQuery: String
}
enum UserOrderBy {
CREATED_AT_ASC
CREATED_AT_DESC
NAME_ASC
NAME_DESC
}
```
### Mutation Design
```graphql
type Mutation {
# Use input types for complex mutations
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(input: UpdateUserInput!): UpdateUserPayload!
deleteUser(id: ID!): DeleteUserPayload!
}
input CreateUserInput {
email: String!
displayName: String!
organizationId: ID
}
# Payload types for consistent responses
type CreateUserPayload {
user: User
errors: [UserError!]!
}
type UserError {
field: String
code: String!
message: String!
}
```
### N+1 Query Prevention
```
STRATEGIES:
1. DataLoader pattern for batching
2. Query complexity analysis and limits
3. Depth limiting
4. Field-level cost calculation
5. Persisted queries for production
```
## API Versioning Strategies
### URL Path Versioning
```
GET /v1/users
GET /v2/users
PROS:
- Explicit and visible
- Easy to route in infrastructure
- Clear in logs and monitoring
CONS:
- URL pollution
- Harder to deprecate gracefully
```
### Header Versioning
```
GET /users
Accept: application/vnd.api+json; version=2
PROS:
- Clean URLs
- Content negotiation friendly
- Easier partial versioning
CONS:
- Less visible
- Harder to test in browser
```
### Query Parameter Versioning
```
GET /users?api-version=2025-01-15
PROS:
- Easy to test
- Visible in URLs
- Date-based versions are intuitive
CONS:
- Clutters query strings
- Easy to forget
```
### Recommended: Dual Approach
```
1. Major versions in URL path: /v1/, /v2/
2. Minor versions via header: API-Version: 2025-01-15
3. Default to latest minor within major
4. Sunset headers for deprecation warnings
```
## Authentication Patterns
### API Keys
```
USAGE: Server-to-server, rate limiting, analytics
TRANSPORT: Header (Authorization: ApiKey xxx) or query param
SECURITY:
- Rotate keys regularly
- Different keys for environments
- Scope keys to specific operations
- Never expose in client-side code
```
### OAuth 2.0 / OIDC
```
FLOWS:
- Authorization Code + PKCE: Web apps, mobile apps
- Client Credentials: Server-to-server
- Device Code: CLI tools, smart TVs
TOKEN HANDLING:
- Short-lived access tokens (15-60 min)
- Refresh tokens for session extension
- Token introspection for validation
- Token revocation endpoint
```
### JWT Best Practices
```
CLAIMS:
{
"iss": "https://auth.example.com",
"sub": "user_123",
"aud": "api.example.com",
"exp": 1705320000,
"iat": 1705316400,
"scope": "read:users write:users"
}
SECURITY:
- Use asymmetric keys (RS256, ES256)
- Validate all claims
- Check token expiration
- Verify audience matches
- Keep tokens stateless when possible
```
## OpenAPI/Swagger Patterns
### Specification Structure
```yaml
openapi: 3.1.0
info:
title: Example API
version: 1.0.0
description: API description with markdown support
contact:
name: API Support
url: https://example.com/support
servers:
- url: https://api.example.com/v1
description: Production
- url: https://api.staging.example.com/v1
description: Staging
security:
- bearerAuth: []
paths:
/users:
get:
operationId: listUsers
summary: List all users
tags: [Users]
# ... operation details
components:
schemas:
User:
type: object
required: [id, email]
properties:
id:
type: string
format: uuid
email:
type: string
format: email
```
### Reusable Components
```yaml
components:
schemas:
# Reusable pagination
PaginationMeta:
type: object
properties:
total:
type: integer
page:
type: integer
perPage:
type: integer
# Reusable error
Error:
type: object
required: [code, message]
properties:
code:
type: string
message:
type: string
parameters:
# Reusable query params
PageParam:
name: page
in: query
schema:
type: integer
default: 1
minimum: 1
responses:
# Reusable responses
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
```
## Best Practices
### Do
- Design APIs for consumers, not implementation convenience
- Use meaningful HTTP status codes
- Provide idempotency keys for non-idempotent operations
- Include rate limit headers (X-RateLimit-Limit, X-RateLimit-Remaining)
- Return Location header for created resources
- Support CORS properly for browser clients
- Document all error codes with resolution steps
- Version your API from day one
- Use HTTPS exclusively
- Implement request validation with clear error messages
### Avoid
- Exposing internal implementation details (database IDs, stack traces)
- Breaking changes without versioning
- Inconsistent naming across endpoints
- Deeply nested URLs (more than 2 levels)
- Using GET for operations with side effects
- Returning different structures for success/error
- Ignoring backward compatibility
- Over-fetching in GraphQL without limits
- Authentication via query parameters (except OAuth callbacks)
- Mixing REST and RPC styles in the same API
## References
- `templates/rest-api-template.md` - REST API specification template
- `templates/graphql-schema-template.md` - GraphQL schema template
Attribution
Comments (0)
No comments yet. Be the first to comment!
