
api-design-patterns
API contract design conventions for FastAPI projects with Pydantic v2. Use during the design phase when planning new API endpoints, defining request/response contracts, designing pagination or filtering, standardizing error responses, or planning API versioning. Covers RESTful naming, HTTP method semantics, Pydantic v2 schema naming conventions (XxxCreate/XxxUpdate/XxxResponse), cursor-based pagination, standard error format, and OpenAPI documentation. Does NOT cover implementation details (use python-backend-expert) or system-level architecture (use system-architecture).
- Installs
- 69
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/hieutrtr/ai1-skills --skill api-design-patterns- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads api-design-patterns automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
API Design Patterns
When to Use
Activate this skill when:
- Designing new API endpoints or modifying existing endpoint contracts
- Defining request/response schemas for a feature
- Standardizing pagination, filtering, or sorting across endpoints
- Designing a consistent error response format
- Planning API versioning or deprecation strategy
- Reviewing API contracts for consistency before implementation
- Documenting endpoint specifications for frontend/backend coordination
Input: If plan.md or architecture.md exists, read for context about the feature scope and architectural decisions. Otherwise, work from the user's request directly.
Output: Write API design to api-design.md. Tell the user: "API design written to api-design.md. Run /task-decomposition to create implementation tasks or /python-backend-expert to implement."
Do NOT use this skill for:
- Writing implementation code (use
python-backend-expert) - System-level architecture decisions (use
system-architecture) - Writing tests for endpoints (use
pytest-patterns) - Frontend data fetching implementation (use
react-frontend-expert)
Instructions
URL Naming Conventions
Resource Naming Rules
- Plural nouns for collections:
/users,/orders,/products - Kebab-case for multi-word resources:
/order-items,/user-profiles - Singular resource by ID:
/users/{user_id},/orders/{order_id} - Maximum 2 nesting levels:
/users/{user_id}/orders(not/users/{user_id}/orders/{order_id}/items/{item_id}) - No verbs in URLs: use HTTP methods instead (
POST /ordersnot/orders/create) - Query parameters for filtering, sorting, pagination:
/users?role=admin&sort=-created_at
URL Structure Template
/{version}/{resource} → Collection (list, create)
/{version}/{resource}/{id} → Single resource (get, update, delete)
/{version}/{resource}/{id}/{sub-resource} → Nested collection
/{version}/{resource}/actions/{action} → Non-CRUD operations (rarely needed)
Naming Examples
| Good | Bad | Reason |
|---|---|---|
GET /v1/users | GET /v1/getUsers | No verbs — HTTP method implies action |
POST /v1/users | POST /v1/user/create | POST to collection = create |
GET /v1/order-items | GET /v1/orderItems | Kebab-case, not camelCase |
GET /v1/users/{id}/orders | GET /v1/users/{id}/orders/{oid}/items | Max 2 nesting levels |
POST /v1/orders/{id}/actions/cancel | POST /v1/cancelOrder/{id} | Action sub-resource for non-CRUD |
HTTP Method Semantics
| Method | Purpose | Request Body | Success Status | Idempotent |
|---|---|---|---|---|
GET | Retrieve resource(s) | None | 200 OK | Yes |
POST | Create new resource | Required | 201 Created | No |
PUT | Full replace | Required (full) | 200 OK | Yes |
PATCH | Partial update | Required (partial) | 200 OK | No* |
DELETE | Remove resource | None | 204 No Content | Yes |
*PATCH is not inherently idempotent but can be made so with proper implementation.
Response headers for creation:
POSTreturning201SHOULD include aLocationheader with the URL of the created resource
Conditional requests:
- Support
If-None-Match/ETagfor caching on GET endpoints with frequently-accessed resources
Schema Naming Conventions (Pydantic v2)
Follow a consistent naming pattern for all Pydantic schemas:
| Pattern | Purpose | Fields |
|---|---|---|
{Resource}Create | POST request body | Writable fields, no id, no timestamps |
{Resource}Update | PUT request body | All writable fields required |
{Resource}Patch | PATCH request body | All fields Optional |
{Resource}Response | Single resource response | All fields including id, timestamps |
{Resource}ListResponse | Paginated list response | items + pagination metadata |
{Resource}Filter | Query parameters | Optional filter fields |
Schema design rules:
- Never expose internal fields (hashed_password, internal_notes) in Response schemas
- Always include
idand timestamps (created_at,updated_at) in Response schemas - Use
model_validate(orm_instance)to convert ORM models to response schemas - Use
model_dump(exclude_unset=True)for PATCH operations to distinguish "not provided" from "set to null" - Reference
references/pydantic-schema-examples.mdfor concrete examples
Pagination
Cursor-Based Pagination (Default)
Use cursor-based pagination for all list endpoints. It is more performant than offset-based for large datasets and avoids the "shifting window" problem.
Request parameters:
GET /v1/users?cursor=eyJpZCI6MTAwfQ&limit=20
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor | str | None | None | Opaque cursor from previous response |
limit | int | 20 | Items per page (max 100) |
Response format:
{
"items": [...],
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
Cursor implementation:
- Encode the last item's sort key (usually
id) as a base64 string - The cursor is opaque to the client — they must not parse or construct it
- Use
WHERE id > :last_id ORDER BY id ASC LIMIT :limit + 1— fetch one extra to determinehas_more
Offset-Based Pagination (When Needed)
Use offset-based only when the client needs to jump to arbitrary pages (e.g., admin tables).
{
"items": [...],
"total": 150,
"page": 2,
"page_size": 20,
"total_pages": 8
}
Filtering and Sorting
Filtering
Use query parameters with field names:
GET /v1/users?role=admin&is_active=true&created_after=2024-01-01
Filtering conventions:
- Exact match:
?field=value - Range:
?field_min=10&field_max=100or?created_after=...&created_before=... - Search:
?q=search+term(for full-text search across multiple fields) - Multiple values:
?status=active&status=pending(OR semantics)
Sorting
Use a sort query parameter with field name and direction prefix:
GET /v1/users?sort=-created_at → descending by created_at
GET /v1/users?sort=name → ascending by name
GET /v1/users?sort=-created_at,name → multi-field sort
Convention: - prefix means descending, no prefix means ascending.
Error Response Format
All API errors follow a consistent format:
{
"detail": "Human-readable error message",
"code": "MACHINE_READABLE_CODE",
"field_errors": [
{
"field": "email",
"message": "Invalid email format",
"code": "INVALID_FORMAT"
}
]
}
Standard Error Codes and Status Mapping
| HTTP Status | When to Use | Example code |
|---|---|---|
400 | Malformed request | BAD_REQUEST |
401 | Missing or invalid authentication | UNAUTHORIZED |
403 | Authenticated but not authorized | FORBIDDEN |
404 | Resource not found | NOT_FOUND |
409 | Conflict (duplicate, version mismatch) | CONFLICT |
422 | Validation error (Pydantic) | VALIDATION_ERROR |
429 | Rate limit exceeded | RATE_LIMITED |
500 | Unexpected server error | INTERNAL_ERROR |
Error schema (Pydantic v2):
class FieldError(BaseModel):
field: str
message: str
code: str
class ErrorResponse(BaseModel):
detail: str
code: str
field_errors: list[FieldError] = []
API Versioning
Strategy: URL Prefix Versioning
/v1/users → Version 1
/v2/users → Version 2
Versioning rules:
- Start with
/v1/for all new APIs - Increment major version only for breaking changes
- Non-breaking changes (new optional fields, new endpoints) do NOT require a new version
- Support at most 2 active versions simultaneously
Breaking changes that require a new version:
- Removing a field from a response
- Changing a field's type
- Making an optional request field required
- Changing the URL structure for existing endpoints
- Changing error response format
Deprecation process:
- Add
Deprecationheader to the old version:Deprecation: true - Add
Sunsetheader with the retirement date:Sunset: Sat, 01 Mar 2026 00:00:00 GMT - Add
Linkheader pointing to the new version:Link: </v2/users>; rel="successor-version" - Log usage of deprecated endpoints for monitoring
- Remove the old version after the sunset date
OpenAPI Documentation
FastAPI generates OpenAPI schemas automatically. Enhance them with:
@router.get(
"/users/{user_id}",
response_model=UserResponse,
summary="Get user by ID",
description="Retrieve a single user's details by their unique identifier.",
responses={
404: {"model": ErrorResponse, "description": "User not found"},
},
tags=["Users"],
)
async def get_user(user_id: int) -> UserResponse:
...
Documentation conventions:
- Every endpoint has a
summary(short) and optionaldescription(detailed) - Document all non-200 responses with their schema
- Group endpoints by
tagsmatching the resource name - Use
response_modelfor automatic response schema documentation
Examples
Designing a Products API Contract
Objective: Design the contract for a /v1/products CRUD endpoint with search and pagination.
Endpoints:
| Method | Path | Description | Request | Response | Status |
|---|---|---|---|---|---|
| GET | /v1/products | List products | Query: cursor, limit, q, category, sort | ProductListResponse | 200 |
| POST | /v1/products | Create product | Body: ProductCreate | ProductResponse | 201 |
| GET | /v1/products/{id} | Get product | — | ProductResponse | 200 |
| PATCH | /v1/products/{id} | Update product | Body: ProductPatch | ProductResponse | 200 |
| DELETE | /v1/products/{id} | Delete product | — | — | 204 |
Schemas:
class ProductCreate(BaseModel):
name: str = Field(min_length=1, max_length=200)
description: str | None = None
price_cents: int = Field(gt=0)
category: str
sku: str = Field(pattern=r"^[A-Z0-9-]+$")
class ProductPatch(BaseModel):
name: str | None = None
description: str | None = None
price_cents: int | None = Field(default=None, gt=0)
category: str | None = None
class ProductResponse(BaseModel):
id: int
name: str
description: str | None
price_cents: int
category: str
sku: str
created_at: datetime
updated_at: datetime
class ProductListResponse(BaseModel):
items: list[ProductResponse]
next_cursor: str | None
has_more: bool
Search and filtering:
GET /v1/products?q=laptop&category=electronics&sort=-price_cents&limit=20
See references/endpoint-catalog-template.md for the full documentation template.
See references/pydantic-schema-examples.md for additional schema examples.
Edge Cases
Bulk Operations
For operations on multiple resources at once:
POST /v1/users/bulk
Request:
{
"items": [
{"email": "a@example.com", "name": "Alice"},
{"email": "b@example.com", "name": "Bob"}
]
}
Response (partial success — status 207):
{
"results": [
{"index": 0, "status": "created", "data": {...}},
{"index": 1, "status": "error", "error": {"detail": "Email already exists", "code": "CONFLICT"}}
],
"succeeded": 1,
"failed": 1
}
Use HTTP 207 Multi-Status when individual items can succeed or fail independently.
File Upload Endpoints
File uploads use multipart/form-data, not JSON:
@router.post("/v1/files", response_model=FileResponse, status_code=201)
async def upload_file(
file: UploadFile,
description: str = Form(default=""),
) -> FileResponse:
...
Validate file size and MIME type before processing. Return 413 Payload Too Large for oversized files.
Long-Running Operations
For operations that cannot complete within a normal request timeout:
-
Return
202 Acceptedwith a status URL:{"status_url": "/v1/jobs/abc123", "estimated_completion": "2024-01-15T10:30:00Z"} -
Client polls the status URL:
GET /v1/jobs/abc123 → {"status": "processing", "progress": 0.65} GET /v1/jobs/abc123 → {"status": "completed", "result_url": "/v1/reports/xyz"}
Sub-Resource Design
When a resource logically belongs to a parent but nesting would exceed 2 levels, use a top-level resource with a filter:
# Instead of: GET /v1/users/{id}/orders/{oid}/items
# Use: GET /v1/order-items?order_id=123
This keeps URLs flat while maintaining the relationship through filtering.
Output File
Write the API design to api-design.md at the project root:
# API Design: [Feature Name]
## Endpoints
| Method | URL | Description | Auth |
|--------|-----|-------------|------|
| GET | /v1/users | List users | Required |
| POST | /v1/users | Create user | Required |
## Request/Response Schemas
### UserCreate
| Field | Type | Required | Validation |
|-------|------|----------|------------|
| email | string | Yes | Valid email |
| name | string | Yes | 1-100 chars |
### UserResponse
| Field | Type | Description |
|-------|------|-------------|
| id | uuid | User ID |
| email | string | User email |
## Error Codes
| Code | HTTP Status | Description |
|------|-------------|-------------|
| USER_NOT_FOUND | 404 | User does not exist |
| EMAIL_EXISTS | 409 | Email already registered |
## Next Steps
- Run `/task-decomposition` to create implementation tasks
- Run `/python-backend-expert` to implement endpoints
FAQ
What does the api-design-patterns skill do?
API contract design conventions for FastAPI projects with Pydantic v2. Use during the design phase when planning new API endpoints, defining request/response contracts, designing pagination or filtering, standardizing error responses, or planning API versioning. Covers RESTful naming, HTTP method semantics, Pydantic v2 schema naming conventions (XxxCreate/XxxUpdate/XxxResponse), cursor-based pagination, standard error format, and OpenAPI documentation. Does NOT cover implementation details (use python-backend-expert) or system-level architecture (use system-architecture).
How do I install api-design-patterns?
Run npx skills add https://github.com/hieutrtr/ai1-skills --skill api-design-patterns in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/api-design-patterns/ (personal) or .claude/skills/api-design-patterns/ (project).
Which AI agents can use api-design-patterns?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is api-design-patterns safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains api-design-patterns?
It is published by hieutrtr in hieutrtr/ai1-skills. It has 69 recorded installs.
More from hieutrtr/ai1-skills
e2e-testing
hieutrtr/ai1-skills
End-to-end testing patterns with Playwright for full-stack Python/React applications. Use when writing E2E tests for complete user workflows (login, CRUD, navigation), critical path regression tests, or cross-browser validation. Covers test structure, page object model, selector strategy (data-testid role label), wait strategies, auth state reuse, test data management, and CI integration. Does NOT cover unit tests or component tests (use pytest-patterns or react-testing-patterns).
code-review-security
hieutrtr/ai1-skills
Open the skill to read its SKILL.md instructions.
react-frontend-expert
hieutrtr/ai1-skills
Open the skill to read its SKILL.md instructions.
react-testing-patterns
hieutrtr/ai1-skills
Open the skill to read its SKILL.md instructions.
python-backend-expert
hieutrtr/ai1-skills
Open the skill to read its SKILL.md instructions.
pytest-patterns
hieutrtr/ai1-skills
Open the skill to read its SKILL.md instructions.
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.