{
  "schemaVersion": "1.0",
  "item": {
    "slug": "api-design-principles",
    "name": "API Design Principles",
    "source": "tencent",
    "type": "skill",
    "category": "开发工具",
    "sourceUrl": "https://clawhub.ai/wpank/api-design-principles",
    "canonicalUrl": "https://clawhub.ai/wpank/api-design-principles",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/api-design-principles",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=api-design-principles",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "README.md",
      "SKILL.md",
      "templates/fastapi-template.py",
      "references/quick-reference.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-23T16:43:11.935Z",
      "expiresAt": "2026-04-30T16:43:11.935Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=4claw-imageboard",
        "contentDisposition": "attachment; filename=\"4claw-imageboard-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null
      },
      "scope": "source",
      "summary": "Source download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this source.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/api-design-principles"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/api-design-principles",
    "agentPageUrl": "https://openagent3.xyz/skills/api-design-principles/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-design-principles/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-design-principles/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "WHAT",
        "body": "Design intuitive, scalable REST and GraphQL APIs that developers love. Covers resource modeling, HTTP semantics, pagination, error handling, versioning, and GraphQL schema patterns."
      },
      {
        "title": "WHEN",
        "body": "Designing new REST or GraphQL APIs\nReviewing API specifications before implementation\nEstablishing API design standards for teams\nRefactoring APIs for better usability\nMigrating between API paradigms"
      },
      {
        "title": "KEYWORDS",
        "body": "REST, GraphQL, API design, HTTP methods, pagination, error handling, versioning, OpenAPI, HATEOAS, schema design"
      },
      {
        "title": "Decision Framework: REST vs GraphQL",
        "body": "Choose REST when...Choose GraphQL when...Simple CRUD operationsComplex nested data requirementsPublic APIs with broad audienceMobile apps needing bandwidth optimizationHeavy caching requirementsClients need to specify exact data shapeTeam is unfamiliar with GraphQLAggregating multiple data sourcesSimple response structuresRapidly evolving frontend requirements"
      },
      {
        "title": "Resource Naming Rules",
        "body": "✓ Plural nouns for collections\n  GET /api/users\n  GET /api/orders\n  GET /api/products\n\n✗ Avoid verbs (let HTTP methods be the verb)\n  POST /api/createUser     ← Wrong\n  POST /api/users          ← Correct\n\n✓ Nested resources (max 2 levels)\n  GET /api/users/{id}/orders\n  \n✗ Avoid deep nesting\n  GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews  ← Too deep\n  GET /api/order-items/{id}/reviews                            ← Better"
      },
      {
        "title": "HTTP Methods and Status Codes",
        "body": "MethodPurposeSuccessCommon ErrorsGETRetrieve200 OK404 Not FoundPOSTCreate201 Created400/422 ValidationPUTReplace200 OK404 Not FoundPATCHPartial update200 OK404 Not FoundDELETERemove204 No Content404/409 Conflict"
      },
      {
        "title": "Complete Status Code Reference",
        "body": "SUCCESS = {\n    200: \"OK\",           # GET, PUT, PATCH success\n    201: \"Created\",      # POST success\n    204: \"No Content\",   # DELETE success\n}\n\nCLIENT_ERROR = {\n    400: \"Bad Request\",           # Malformed syntax\n    401: \"Unauthorized\",          # Missing/invalid auth\n    403: \"Forbidden\",             # Valid auth, no permission\n    404: \"Not Found\",             # Resource doesn't exist\n    409: \"Conflict\",              # State conflict (duplicate email)\n    422: \"Unprocessable Entity\",  # Validation errors\n    429: \"Too Many Requests\",     # Rate limited\n}\n\nSERVER_ERROR = {\n    500: \"Internal Server Error\",\n    503: \"Service Unavailable\",   # Temporary downtime\n}"
      },
      {
        "title": "Pagination",
        "body": "Offset-Based (Simple)\n\nGET /api/users?page=2&page_size=20\n\n{\n  \"items\": [...],\n  \"page\": 2,\n  \"page_size\": 20,\n  \"total\": 150,\n  \"pages\": 8\n}\n\nCursor-Based (For Large Datasets)\n\nGET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ\n\n{\n  \"items\": [...],\n  \"next_cursor\": \"eyJpZCI6MTQzfQ\",\n  \"has_more\": true\n}"
      },
      {
        "title": "Filtering and Sorting",
        "body": "# Filtering\nGET /api/users?status=active&role=admin\n\n# Sorting (- prefix for descending)\nGET /api/users?sort=-created_at,name\n\n# Search\nGET /api/users?search=john\n\n# Field selection\nGET /api/users?fields=id,name,email"
      },
      {
        "title": "Error Response Format",
        "body": "Always use consistent structure:\n\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\"field\": \"email\", \"message\": \"Invalid email format\"}\n    ],\n    \"timestamp\": \"2025-10-16T12:00:00Z\"\n  }\n}"
      },
      {
        "title": "FastAPI Implementation",
        "body": "from fastapi import FastAPI, Query, Path, HTTPException, status\nfrom pydantic import BaseModel, Field, EmailStr\nfrom typing import Optional, List\nfrom datetime import datetime\n\napp = FastAPI(title=\"API\", version=\"1.0.0\")\n\n# Models\nclass UserCreate(BaseModel):\n    email: EmailStr\n    name: str = Field(..., min_length=1, max_length=100)\n\nclass User(BaseModel):\n    id: str\n    email: str\n    name: str\n    created_at: datetime\n\nclass PaginatedResponse(BaseModel):\n    items: List[User]\n    total: int\n    page: int\n    page_size: int\n    pages: int\n\n# Endpoints\n@app.get(\"/api/users\", response_model=PaginatedResponse)\nasync def list_users(\n    page: int = Query(1, ge=1),\n    page_size: int = Query(20, ge=1, le=100),\n    status: Optional[str] = Query(None),\n    search: Optional[str] = Query(None)\n):\n    \"\"\"List users with pagination and filtering.\"\"\"\n    total = await count_users(status=status, search=search)\n    offset = (page - 1) * page_size\n    users = await fetch_users(limit=page_size, offset=offset, status=status, search=search)\n    \n    return PaginatedResponse(\n        items=users,\n        total=total,\n        page=page,\n        page_size=page_size,\n        pages=(total + page_size - 1) // page_size\n    )\n\n@app.post(\"/api/users\", response_model=User, status_code=status.HTTP_201_CREATED)\nasync def create_user(user: UserCreate):\n    \"\"\"Create new user.\"\"\"\n    if await user_exists(user.email):\n        raise HTTPException(\n            status_code=status.HTTP_409_CONFLICT,\n            detail={\"code\": \"EMAIL_EXISTS\", \"message\": \"Email already registered\"}\n        )\n    return await save_user(user)\n\n@app.get(\"/api/users/{user_id}\", response_model=User)\nasync def get_user(user_id: str = Path(...)):\n    \"\"\"Get user by ID.\"\"\"\n    user = await fetch_user(user_id)\n    if not user:\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    return user\n\n@app.delete(\"/api/users/{user_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_user(user_id: str):\n    \"\"\"Delete user.\"\"\"\n    if not await fetch_user(user_id):\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    await remove_user(user_id)"
      },
      {
        "title": "Schema Structure",
        "body": "# Types\ntype User {\n  id: ID!\n  email: String!\n  name: String!\n  createdAt: DateTime!\n  orders(first: Int = 20, after: String): OrderConnection!\n}\n\n# Pagination (Relay-style)\ntype OrderConnection {\n  edges: [OrderEdge!]!\n  pageInfo: PageInfo!\n  totalCount: Int!\n}\n\ntype OrderEdge {\n  node: Order!\n  cursor: String!\n}\n\ntype PageInfo {\n  hasNextPage: Boolean!\n  hasPreviousPage: Boolean!\n  startCursor: String\n  endCursor: String\n}\n\n# Queries\ntype Query {\n  user(id: ID!): User\n  users(first: Int = 20, after: String, search: String): UserConnection!\n}\n\n# Mutations with Input/Payload pattern\ninput CreateUserInput {\n  email: String!\n  name: String!\n  password: String!\n}\n\ntype CreateUserPayload {\n  user: User\n  errors: [Error!]\n}\n\ntype Error {\n  field: String\n  message: String!\n  code: String!\n}\n\ntype Mutation {\n  createUser(input: CreateUserInput!): CreateUserPayload!\n}"
      },
      {
        "title": "DataLoader (Prevent N+1)",
        "body": "from aiodataloader import DataLoader\n\nclass UserLoader(DataLoader):\n    async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:\n        \"\"\"Load multiple users in single query.\"\"\"\n        users = await fetch_users_by_ids(user_ids)\n        user_map = {user[\"id\"]: user for user in users}\n        return [user_map.get(uid) for uid in user_ids]\n\n# In resolver\n@user_type.field(\"orders\")\nasync def resolve_orders(user: dict, info):\n    loader = info.context[\"loaders\"][\"orders_by_user\"]\n    return await loader.load(user[\"id\"])"
      },
      {
        "title": "Query Protection",
        "body": "# Depth limiting\nMAX_QUERY_DEPTH = 5\n\n# Complexity limiting\nMAX_QUERY_COMPLEXITY = 100\n\n# Timeout\nQUERY_TIMEOUT_SECONDS = 10"
      },
      {
        "title": "URL Versioning (Recommended)",
        "body": "/api/v1/users\n/api/v2/users\n\nPros: Clear, easy to route, cacheable\nCons: Multiple URLs for same resource"
      },
      {
        "title": "Header Versioning",
        "body": "GET /api/users\nAccept: application/vnd.api+json; version=2\n\nPros: Clean URLs\nCons: Less visible, harder to test"
      },
      {
        "title": "Deprecation Strategy",
        "body": "Add deprecation headers: Deprecation: true\nDocument migration path\nGive 6-12 months notice\nMonitor usage before removal"
      },
      {
        "title": "Headers",
        "body": "X-RateLimit-Limit: 1000\nX-RateLimit-Remaining: 742\nX-RateLimit-Reset: 1640000000\n\n# When limited:\n429 Too Many Requests\nRetry-After: 3600"
      },
      {
        "title": "Implementation",
        "body": "from datetime import datetime, timedelta\n\nclass RateLimiter:\n    def __init__(self, calls: int, period: int):\n        self.calls = calls\n        self.period = period\n        self.cache = {}\n    \n    def check(self, key: str) -> tuple[bool, dict]:\n        now = datetime.now()\n        if key not in self.cache:\n            self.cache[key] = []\n        \n        # Remove old requests\n        cutoff = now - timedelta(seconds=self.period)\n        self.cache[key] = [ts for ts in self.cache[key] if ts > cutoff]\n        \n        remaining = self.calls - len(self.cache[key])\n        \n        if remaining <= 0:\n            return False, {\"limit\": self.calls, \"remaining\": 0}\n        \n        self.cache[key].append(now)\n        return True, {\"limit\": self.calls, \"remaining\": remaining - 1}"
      },
      {
        "title": "Resources",
        "body": "Nouns, not verbs\n Plural for collections\n Max 2 levels nesting"
      },
      {
        "title": "HTTP",
        "body": "Correct method for each action\n Correct status codes\n Idempotent operations are idempotent"
      },
      {
        "title": "Data",
        "body": "All collections paginated\n Filtering/sorting supported\n Error format consistent"
      },
      {
        "title": "Security",
        "body": "Authentication defined\n Rate limiting configured\n Input validation on all fields\n HTTPS enforced"
      },
      {
        "title": "Documentation",
        "body": "OpenAPI spec generated\n All endpoints documented\n Examples provided"
      },
      {
        "title": "NEVER",
        "body": "Verbs in URLs: /api/getUser → use /api/users/{id} with GET\nPOST for Retrieval: Use GET for safe, idempotent reads\nInconsistent Errors: Always same error format\nUnbounded Lists: Always paginate collections\nSecrets in URLs: Query params are logged\nBreaking Changes Without Versioning: Plan for evolution from day 1\nDatabase Schema as API: API should be stable even when schema changes\nIgnoring HTTP Semantics: Status codes and methods have meaning"
      }
    ],
    "body": "API Design Principles\nWHAT\n\nDesign intuitive, scalable REST and GraphQL APIs that developers love. Covers resource modeling, HTTP semantics, pagination, error handling, versioning, and GraphQL schema patterns.\n\nWHEN\nDesigning new REST or GraphQL APIs\nReviewing API specifications before implementation\nEstablishing API design standards for teams\nRefactoring APIs for better usability\nMigrating between API paradigms\nKEYWORDS\n\nREST, GraphQL, API design, HTTP methods, pagination, error handling, versioning, OpenAPI, HATEOAS, schema design\n\nDecision Framework: REST vs GraphQL\nChoose REST when...\tChoose GraphQL when...\nSimple CRUD operations\tComplex nested data requirements\nPublic APIs with broad audience\tMobile apps needing bandwidth optimization\nHeavy caching requirements\tClients need to specify exact data shape\nTeam is unfamiliar with GraphQL\tAggregating multiple data sources\nSimple response structures\tRapidly evolving frontend requirements\nREST API Design\nResource Naming Rules\n✓ Plural nouns for collections\n  GET /api/users\n  GET /api/orders\n  GET /api/products\n\n✗ Avoid verbs (let HTTP methods be the verb)\n  POST /api/createUser     ← Wrong\n  POST /api/users          ← Correct\n\n✓ Nested resources (max 2 levels)\n  GET /api/users/{id}/orders\n  \n✗ Avoid deep nesting\n  GET /api/users/{id}/orders/{orderId}/items/{itemId}/reviews  ← Too deep\n  GET /api/order-items/{id}/reviews                            ← Better\n\nHTTP Methods and Status Codes\nMethod\tPurpose\tSuccess\tCommon Errors\nGET\tRetrieve\t200 OK\t404 Not Found\nPOST\tCreate\t201 Created\t400/422 Validation\nPUT\tReplace\t200 OK\t404 Not Found\nPATCH\tPartial update\t200 OK\t404 Not Found\nDELETE\tRemove\t204 No Content\t404/409 Conflict\nComplete Status Code Reference\nSUCCESS = {\n    200: \"OK\",           # GET, PUT, PATCH success\n    201: \"Created\",      # POST success\n    204: \"No Content\",   # DELETE success\n}\n\nCLIENT_ERROR = {\n    400: \"Bad Request\",           # Malformed syntax\n    401: \"Unauthorized\",          # Missing/invalid auth\n    403: \"Forbidden\",             # Valid auth, no permission\n    404: \"Not Found\",             # Resource doesn't exist\n    409: \"Conflict\",              # State conflict (duplicate email)\n    422: \"Unprocessable Entity\",  # Validation errors\n    429: \"Too Many Requests\",     # Rate limited\n}\n\nSERVER_ERROR = {\n    500: \"Internal Server Error\",\n    503: \"Service Unavailable\",   # Temporary downtime\n}\n\nPagination\nOffset-Based (Simple)\nGET /api/users?page=2&page_size=20\n\n{\n  \"items\": [...],\n  \"page\": 2,\n  \"page_size\": 20,\n  \"total\": 150,\n  \"pages\": 8\n}\n\nCursor-Based (For Large Datasets)\nGET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ\n\n{\n  \"items\": [...],\n  \"next_cursor\": \"eyJpZCI6MTQzfQ\",\n  \"has_more\": true\n}\n\nFiltering and Sorting\n# Filtering\nGET /api/users?status=active&role=admin\n\n# Sorting (- prefix for descending)\nGET /api/users?sort=-created_at,name\n\n# Search\nGET /api/users?search=john\n\n# Field selection\nGET /api/users?fields=id,name,email\n\nError Response Format\n\nAlways use consistent structure:\n\n{\n  \"error\": {\n    \"code\": \"VALIDATION_ERROR\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\"field\": \"email\", \"message\": \"Invalid email format\"}\n    ],\n    \"timestamp\": \"2025-10-16T12:00:00Z\"\n  }\n}\n\nFastAPI Implementation\nfrom fastapi import FastAPI, Query, Path, HTTPException, status\nfrom pydantic import BaseModel, Field, EmailStr\nfrom typing import Optional, List\nfrom datetime import datetime\n\napp = FastAPI(title=\"API\", version=\"1.0.0\")\n\n# Models\nclass UserCreate(BaseModel):\n    email: EmailStr\n    name: str = Field(..., min_length=1, max_length=100)\n\nclass User(BaseModel):\n    id: str\n    email: str\n    name: str\n    created_at: datetime\n\nclass PaginatedResponse(BaseModel):\n    items: List[User]\n    total: int\n    page: int\n    page_size: int\n    pages: int\n\n# Endpoints\n@app.get(\"/api/users\", response_model=PaginatedResponse)\nasync def list_users(\n    page: int = Query(1, ge=1),\n    page_size: int = Query(20, ge=1, le=100),\n    status: Optional[str] = Query(None),\n    search: Optional[str] = Query(None)\n):\n    \"\"\"List users with pagination and filtering.\"\"\"\n    total = await count_users(status=status, search=search)\n    offset = (page - 1) * page_size\n    users = await fetch_users(limit=page_size, offset=offset, status=status, search=search)\n    \n    return PaginatedResponse(\n        items=users,\n        total=total,\n        page=page,\n        page_size=page_size,\n        pages=(total + page_size - 1) // page_size\n    )\n\n@app.post(\"/api/users\", response_model=User, status_code=status.HTTP_201_CREATED)\nasync def create_user(user: UserCreate):\n    \"\"\"Create new user.\"\"\"\n    if await user_exists(user.email):\n        raise HTTPException(\n            status_code=status.HTTP_409_CONFLICT,\n            detail={\"code\": \"EMAIL_EXISTS\", \"message\": \"Email already registered\"}\n        )\n    return await save_user(user)\n\n@app.get(\"/api/users/{user_id}\", response_model=User)\nasync def get_user(user_id: str = Path(...)):\n    \"\"\"Get user by ID.\"\"\"\n    user = await fetch_user(user_id)\n    if not user:\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    return user\n\n@app.delete(\"/api/users/{user_id}\", status_code=status.HTTP_204_NO_CONTENT)\nasync def delete_user(user_id: str):\n    \"\"\"Delete user.\"\"\"\n    if not await fetch_user(user_id):\n        raise HTTPException(status_code=404, detail=\"User not found\")\n    await remove_user(user_id)\n\nGraphQL API Design\nSchema Structure\n# Types\ntype User {\n  id: ID!\n  email: String!\n  name: String!\n  createdAt: DateTime!\n  orders(first: Int = 20, after: String): OrderConnection!\n}\n\n# Pagination (Relay-style)\ntype OrderConnection {\n  edges: [OrderEdge!]!\n  pageInfo: PageInfo!\n  totalCount: Int!\n}\n\ntype OrderEdge {\n  node: Order!\n  cursor: String!\n}\n\ntype PageInfo {\n  hasNextPage: Boolean!\n  hasPreviousPage: Boolean!\n  startCursor: String\n  endCursor: String\n}\n\n# Queries\ntype Query {\n  user(id: ID!): User\n  users(first: Int = 20, after: String, search: String): UserConnection!\n}\n\n# Mutations with Input/Payload pattern\ninput CreateUserInput {\n  email: String!\n  name: String!\n  password: String!\n}\n\ntype CreateUserPayload {\n  user: User\n  errors: [Error!]\n}\n\ntype Error {\n  field: String\n  message: String!\n  code: String!\n}\n\ntype Mutation {\n  createUser(input: CreateUserInput!): CreateUserPayload!\n}\n\nDataLoader (Prevent N+1)\nfrom aiodataloader import DataLoader\n\nclass UserLoader(DataLoader):\n    async def batch_load_fn(self, user_ids: List[str]) -> List[Optional[dict]]:\n        \"\"\"Load multiple users in single query.\"\"\"\n        users = await fetch_users_by_ids(user_ids)\n        user_map = {user[\"id\"]: user for user in users}\n        return [user_map.get(uid) for uid in user_ids]\n\n# In resolver\n@user_type.field(\"orders\")\nasync def resolve_orders(user: dict, info):\n    loader = info.context[\"loaders\"][\"orders_by_user\"]\n    return await loader.load(user[\"id\"])\n\nQuery Protection\n# Depth limiting\nMAX_QUERY_DEPTH = 5\n\n# Complexity limiting\nMAX_QUERY_COMPLEXITY = 100\n\n# Timeout\nQUERY_TIMEOUT_SECONDS = 10\n\nVersioning Strategies\nURL Versioning (Recommended)\n/api/v1/users\n/api/v2/users\n\n\nPros: Clear, easy to route, cacheable Cons: Multiple URLs for same resource\n\nHeader Versioning\nGET /api/users\nAccept: application/vnd.api+json; version=2\n\n\nPros: Clean URLs Cons: Less visible, harder to test\n\nDeprecation Strategy\nAdd deprecation headers: Deprecation: true\nDocument migration path\nGive 6-12 months notice\nMonitor usage before removal\nRate Limiting\nHeaders\nX-RateLimit-Limit: 1000\nX-RateLimit-Remaining: 742\nX-RateLimit-Reset: 1640000000\n\n# When limited:\n429 Too Many Requests\nRetry-After: 3600\n\nImplementation\nfrom datetime import datetime, timedelta\n\nclass RateLimiter:\n    def __init__(self, calls: int, period: int):\n        self.calls = calls\n        self.period = period\n        self.cache = {}\n    \n    def check(self, key: str) -> tuple[bool, dict]:\n        now = datetime.now()\n        if key not in self.cache:\n            self.cache[key] = []\n        \n        # Remove old requests\n        cutoff = now - timedelta(seconds=self.period)\n        self.cache[key] = [ts for ts in self.cache[key] if ts > cutoff]\n        \n        remaining = self.calls - len(self.cache[key])\n        \n        if remaining <= 0:\n            return False, {\"limit\": self.calls, \"remaining\": 0}\n        \n        self.cache[key].append(now)\n        return True, {\"limit\": self.calls, \"remaining\": remaining - 1}\n\nPre-Implementation Checklist\nResources\n Nouns, not verbs\n Plural for collections\n Max 2 levels nesting\nHTTP\n Correct method for each action\n Correct status codes\n Idempotent operations are idempotent\nData\n All collections paginated\n Filtering/sorting supported\n Error format consistent\nSecurity\n Authentication defined\n Rate limiting configured\n Input validation on all fields\n HTTPS enforced\nDocumentation\n OpenAPI spec generated\n All endpoints documented\n Examples provided\nNEVER\nVerbs in URLs: /api/getUser → use /api/users/{id} with GET\nPOST for Retrieval: Use GET for safe, idempotent reads\nInconsistent Errors: Always same error format\nUnbounded Lists: Always paginate collections\nSecrets in URLs: Query params are logged\nBreaking Changes Without Versioning: Plan for evolution from day 1\nDatabase Schema as API: API should be stable even when schema changes\nIgnoring HTTP Semantics: Status codes and methods have meaning"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/wpank/api-design-principles",
    "publisherUrl": "https://clawhub.ai/wpank/api-design-principles",
    "owner": "wpank",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/api-design-principles",
    "downloadUrl": "https://openagent3.xyz/downloads/api-design-principles",
    "agentUrl": "https://openagent3.xyz/skills/api-design-principles/agent",
    "manifestUrl": "https://openagent3.xyz/skills/api-design-principles/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/api-design-principles/agent.md"
  }
}