MCPInstall

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
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads api-design-patterns automatically when relevant.

  3. 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

  1. Plural nouns for collections: /users, /orders, /products
  2. Kebab-case for multi-word resources: /order-items, /user-profiles
  3. Singular resource by ID: /users/{user_id}, /orders/{order_id}
  4. Maximum 2 nesting levels: /users/{user_id}/orders (not /users/{user_id}/orders/{order_id}/items/{item_id})
  5. No verbs in URLs: use HTTP methods instead (POST /orders not /orders/create)
  6. 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

GoodBadReason
GET /v1/usersGET /v1/getUsersNo verbs — HTTP method implies action
POST /v1/usersPOST /v1/user/createPOST to collection = create
GET /v1/order-itemsGET /v1/orderItemsKebab-case, not camelCase
GET /v1/users/{id}/ordersGET /v1/users/{id}/orders/{oid}/itemsMax 2 nesting levels
POST /v1/orders/{id}/actions/cancelPOST /v1/cancelOrder/{id}Action sub-resource for non-CRUD

HTTP Method Semantics

MethodPurposeRequest BodySuccess StatusIdempotent
GETRetrieve resource(s)None200 OKYes
POSTCreate new resourceRequired201 CreatedNo
PUTFull replaceRequired (full)200 OKYes
PATCHPartial updateRequired (partial)200 OKNo*
DELETERemove resourceNone204 No ContentYes

*PATCH is not inherently idempotent but can be made so with proper implementation.

Response headers for creation:

  • POST returning 201 SHOULD include a Location header with the URL of the created resource

Conditional requests:

  • Support If-None-Match / ETag for caching on GET endpoints with frequently-accessed resources

Schema Naming Conventions (Pydantic v2)

Follow a consistent naming pattern for all Pydantic schemas:

PatternPurposeFields
{Resource}CreatePOST request bodyWritable fields, no id, no timestamps
{Resource}UpdatePUT request bodyAll writable fields required
{Resource}PatchPATCH request bodyAll fields Optional
{Resource}ResponseSingle resource responseAll fields including id, timestamps
{Resource}ListResponsePaginated list responseitems + pagination metadata
{Resource}FilterQuery parametersOptional filter fields

Schema design rules:

  • Never expose internal fields (hashed_password, internal_notes) in Response schemas
  • Always include id and 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.md for 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
ParameterTypeDefaultDescription
cursorstr | NoneNoneOpaque cursor from previous response
limitint20Items 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 determine has_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=100 or ?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 StatusWhen to UseExample code
400Malformed requestBAD_REQUEST
401Missing or invalid authenticationUNAUTHORIZED
403Authenticated but not authorizedFORBIDDEN
404Resource not foundNOT_FOUND
409Conflict (duplicate, version mismatch)CONFLICT
422Validation error (Pydantic)VALIDATION_ERROR
429Rate limit exceededRATE_LIMITED
500Unexpected server errorINTERNAL_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:

  1. Start with /v1/ for all new APIs
  2. Increment major version only for breaking changes
  3. Non-breaking changes (new optional fields, new endpoints) do NOT require a new version
  4. 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:

  1. Add Deprecation header to the old version: Deprecation: true
  2. Add Sunset header with the retirement date: Sunset: Sat, 01 Mar 2026 00:00:00 GMT
  3. Add Link header pointing to the new version: Link: </v2/users>; rel="successor-version"
  4. Log usage of deprecated endpoints for monitoring
  5. 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 optional description (detailed)
  • Document all non-200 responses with their schema
  • Group endpoints by tags matching the resource name
  • Use response_model for 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:

MethodPathDescriptionRequestResponseStatus
GET/v1/productsList productsQuery: cursor, limit, q, category, sortProductListResponse200
POST/v1/productsCreate productBody: ProductCreateProductResponse201
GET/v1/products/{id}Get product—ProductResponse200
PATCH/v1/products/{id}Update productBody: ProductPatchProductResponse200
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:

  1. Return 202 Accepted with a status URL:

    {"status_url": "/v1/jobs/abc123", "estimated_completion": "2024-01-15T10:30:00Z"}
    
  2. 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

View all →

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).

Testing & QA1.6K installs

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.

Frontend & Design203 installs

react-testing-patterns

hieutrtr/ai1-skills

Open the skill to read its SKILL.md instructions.

Frontend & Design136 installs

python-backend-expert

hieutrtr/ai1-skills

Open the skill to read its SKILL.md instructions.

Backend & APIs127 installs

pytest-patterns

hieutrtr/ai1-skills

Open the skill to read its SKILL.md instructions.

Testing & QA118 installs

Popular backend & apis skills

View category →

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.

OfficialBackend & APIs4.1M installs

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.

OfficialBackend & APIs566.7K installs

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).

Backend & APIs514.8K installs

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.

Backend & APIs453.3K installs

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).

OfficialBackend & APIs336.6K installs

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.

Backend & APIs237.1K installs