← Back to list

api-design-patterns
by ils15
GitHub Copilot agents and instructions for reuse across projects
⭐ 0🍴 0📅 Jan 23, 2026
SKILL.md
name: api-design-patterns description: Design RESTful APIs with proper HTTP methods, status codes, pagination, filtering, error responses, and OpenAPI documentation. Follows FastAPI best practices with Pydantic validation.
API Design Patterns Skill
When to Use
Use this skill when:
- Designing new API endpoints
- Reviewing API consistency
- Implementing pagination and filtering
- Creating error response formats
- Writing OpenAPI documentation
- Validating HTTP status codes
- Building request/response schemas
RESTful Design Principles
1. HTTP Methods
GET /users - List users
GET /users/{id} - Get single user
POST /users - Create user
PUT /users/{id} - Full update
PATCH /users/{id} - Partial update
DELETE /users/{id} - Delete user
2. Status Codes
2xx Success:
- 200 OK - Successful GET, PUT, PATCH
- 201 Created - Successful POST
- 204 No Content - Successful DELETE
4xx Client Errors:
- 400 Bad Request - Invalid input
- 401 Unauthorized - Not authenticated
- 403 Forbidden - Not authorized
- 404 Not Found - Resource not found
- 409 Conflict - Duplicate resource
- 422 Unprocessable - Validation error
5xx Server Errors:
- 500 Internal Error - Unexpected error
- 503 Service Unavail - Maintenance
3. Pagination Pattern
from pydantic import BaseModel
from typing import Generic, TypeVar, List
T = TypeVar('T')
class PaginatedResponse(BaseModel, Generic[T]):
items: List[T]
total: int
page: int
size: int
pages: int
class Config:
from_attributes = True
# Endpoint
@router.get("/users", response_model=PaginatedResponse[UserResponse])
async def list_users(
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
):
total = await user_service.count()
items = await user_service.get_page(page, size)
return PaginatedResponse(
items=items,
total=total,
page=page,
size=size,
pages=(total + size - 1) // size
)
4. Filtering Pattern
from typing import Optional
from fastapi import Query
@router.get("/products")
async def list_products(
# Search
q: Optional[str] = Query(None, min_length=2, max_length=100),
# Filters
category: Optional[str] = None,
min_price: Optional[float] = Query(None, ge=0),
max_price: Optional[float] = Query(None, ge=0),
in_stock: Optional[bool] = None,
# Sorting
sort_by: str = Query("created_at", regex="^(name|price|created_at)$"),
order: str = Query("desc", regex="^(asc|desc)$"),
# Pagination
page: int = Query(1, ge=1),
size: int = Query(20, ge=1, le=100),
):
return await product_service.search(
q=q, category=category,
min_price=min_price, max_price=max_price,
in_stock=in_stock,
sort_by=sort_by, order=order,
page=page, size=size
)
5. Error Response Format
from pydantic import BaseModel
from typing import Optional, List
class ErrorDetail(BaseModel):
loc: List[str]
msg: str
type: str
class ErrorResponse(BaseModel):
error: str
message: str
details: Optional[List[ErrorDetail]] = None
request_id: Optional[str] = None
# Usage
@app.exception_handler(HTTPException)
async def http_exception_handler(request, exc):
return JSONResponse(
status_code=exc.status_code,
content=ErrorResponse(
error=exc.detail,
message=str(exc.detail),
request_id=request.state.request_id
).model_dump()
)
6. Request/Response Schemas
from pydantic import BaseModel, EmailStr, Field
from datetime import datetime
from typing import Optional
# Create (input)
class UserCreate(BaseModel):
email: EmailStr
name: str = Field(..., min_length=2, max_length=100)
password: str = Field(..., min_length=8)
# Update (partial)
class UserUpdate(BaseModel):
email: Optional[EmailStr] = None
name: Optional[str] = Field(None, min_length=2, max_length=100)
# Response (output)
class UserResponse(BaseModel):
id: int
email: str
name: str
created_at: datetime
class Config:
from_attributes = True
# List response
class UserListResponse(BaseModel):
id: int
name: str # Minimal fields for list
7. OpenAPI Documentation
@router.post(
"/users",
response_model=UserResponse,
status_code=201,
summary="Create a new user",
description="Register a new user account with email and password.",
responses={
201: {"description": "User created successfully"},
400: {"description": "Invalid input data"},
409: {"description": "Email already exists"},
},
tags=["users"],
)
async def create_user(user: UserCreate):
"""
Create a new user with the following information:
- **email**: valid email address (unique)
- **name**: user display name (2-100 chars)
- **password**: secure password (min 8 chars)
"""
return await user_service.create(user)
Output Format
## API Design Review
### Endpoints Analyzed
- GET /users - ✅ Correct
- POST /users - ⚠️ Missing 409 response
- DELETE /users/{id} - ❌ Returns 200 (should be 204)
### Issues Found
1. [Endpoint] - [Issue] - [Fix]
### Recommendations
1. Add pagination to list endpoints
2. Standardize error response format
3. Add request_id to all responses
Example Usage
@backend Design CRUD endpoints for products resource
@backend Review API consistency across all endpoints
@backend Add pagination to the orders list endpoint
@backend Create error response schema for the API
Score
Total Score
40/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
Reviews
💬
Reviews coming soon