Skip to content
Development
Skill

/api-patterns

API design: naming, versioning, pagination, idempotency, OpenAPI, error contracts and safe retries. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, error response, HTTP status, rate limit.

BOOST
From plugin
ai-toolkit
176117 skills44 agents
Install
$ npx -y skills add softspark/ai-toolkit --skill api-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/api-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

API design: naming, versioning, pagination, idempotency, OpenAPI, error contracts and safe retries. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, error response, HTTP status, rate limit.

SKILL.md

api-patterns.SKILL.md
name: api-patterns
description: "API design: naming, versioning, pagination, idempotency, OpenAPI, error contracts and safe retries. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, error response, HTTP status, rate limit."
effort: medium
user-invocable: false
allowed-tools: Read

API Patterns Skill

REST API Design

Resource Naming

# Collection
GET    /api/v1/documents        # List documents
POST   /api/v1/documents        # Create document

# Single resource
GET    /api/v1/documents/{id}   # Get document
PUT    /api/v1/documents/{id}   # Replace document
PATCH  /api/v1/documents/{id}   # Update document
DELETE /api/v1/documents/{id}   # Delete document

# Nested resources
GET    /api/v1/users/{id}/documents  # User's documents

HTTP Status Codes

| Code | Meaning | When to Use | |------|---------|-------------| | 200 | OK | Successful GET/PUT/PATCH | | 201 | Created | Successful POST | | 204 | No Content | Successful DELETE | | 400 | Bad Request | Malformed request or an established domain refusal | | 401 | Unauthorized | Missing/invalid auth | | 403 | Forbidden | No permission | | 404 | Not Found | Resource doesn't exist | | 405 | Method Not Allowed | Unsupported method; preserve Allow | | 409 | Conflict | Duplicate resource or current-state conflict | | 412 | Precondition Failed | Supplied concurrency version is stale | | 422 | Unprocessable | Validation error | | 428 | Precondition Required | Required concurrency precondition is missing | | 429 | Too Many Requests | Rate limited | | 500 | Internal Error | Server error | | 503 | Service Unavailable | Dependency temporarily unavailable |

Response Format

Follow the host's existing resource and collection contract. This envelope is illustrative; do not impose it on a framework that already defines another shape.

{
  "data": {
    "id": "123",
    "type": "document",
    "attributes": {
      "title": "Example",
      "content": "..."
    }
  },
  "meta": {
    "total": 100,
    "page": 1,
    "per_page": 10
  }
}

Error Response

Use the established error representation, which may be problem details, framework validation errors, or a domain-specific envelope like this example:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": [
      {"field": "title", "message": "Title is required"},
      {"field": "limit", "message": "Must be between 1 and 100"}
    ]
  }
}

When implementing or reviewing failure paths, read [Error contracts and safe retries](reference/error-contracts.md). Classify from the original cause, preserve public codes and JSON types, and distinguish an unknown operation outcome from a confirmed refusal. Do not turn arbitrary server failures into invalid-input responses.

When adding client-side validation, read `reference/input-validation.md` from the installed `security-patterns` skill. Resolve that skill through the current client's catalog, since adapters may namespace skill directory names. Use one authoritative rule source and prove parity at the request boundary.

---

FastAPI Implementation

from fastapi import FastAPI, HTTPException, Query, Path
from pydantic import BaseModel, Field

app = FastAPI(title="RAG-MCP API", version="1.0.0")

class InvalidSearchQuery(Exception):
    """Known request-level refusal from the application-owned search adapter."""

class SearchRequest(BaseModel):
    query: str = Field(..., min_length=1, description="Search query")
    limit: int = Field(10, ge=1, le=100, description="Max results")

class SearchResult(BaseModel):
    id: str
    title: str
    score: float
    content: str

class SearchResponse(BaseModel):
    results: list[SearchResult]
    total: int

@app.post("/api/v1/search", response_model=SearchResponse)
async def search(request: SearchRequest):
    """Search the knowledge base.

    Args:
        request: Search parameters

    Returns:
        Search results with scores
    """
    try:
        results = await perform_search(request.query, request.limit)
        return SearchResponse(results=results, total=len(results))
    except InvalidSearchQuery as exc:
        raise HTTPException(
            status_code=400,
            detail="The search query is not supported. Check its syntax.",
        ) from exc

---

Parameter Documentation Conventions

The same rules apply to OpenAPI `description` fields, Pydantic `Field(description=...)`, and MCP tool parameters: the description should encode the *workflow*, not just restate the type. A consumer (human or LLM) reads it to know how to supply a valid value, not what language primitive it is.

Prefer enums with per-value descriptions for closed sets

A free-form `string` for `status` forces the caller to guess valid values. Constrain it and document each one:

class ListReposRequest(BaseModel):
    visibility: Literal["PUBLIC", "PRIVATE", "INTERNAL"] = Field(
        "PUBLIC",
        description=(
            "Repository visibility filter. "
            "PUBLIC = visible to anyone; "
            "PRIVATE = only members with explicit access; "
            "INTERNAL = visible to all org members (Enterprise only)."
        ),
    )

In OpenAPI, pair `enum` with the value meanings in the description (or `x-enum-descriptions` if your tooling renders it). Avoid documenting a closed set as plain `string` — the caller cannot tell `INTERNAL` is valid but `internal` is not.

Encode cross-field dependencies in the description

Document cross-field requirements where the dependent field is defined and encode them in the supported schema dialect. JSON Schema supports conditional requirements with `dependentRequired` or `if`/`then`; application state and opaque-token provenance still require prose and runtime checks. See [conditional schema validation](https://json-schema.org/understanding-json-schema/reference/conditionals).

cursor: str | None = Field(
    None,
    desc
Read more
Ships withai-toolkit

AI coding toolkit with machine-enforced safety, 116 skills, 44 agents, lifecycle hooks, persona presets, opt-in plugin packs, and benchmark tooling.

Get the whole plugin

Other skills on ai-toolkit.