[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6JCNHG95NGFHHVFXFKPB8D7":3,"skill-same:01M6JCNHG95NGFHHVFXFKPB8D7":81},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":33,"publishedAt":53,"linkOnly":54,"markdownContent":55,"markdownBody":56,"frontMatter":57,"refName":59,"commitSha":60,"skillDirectory":61,"forksCount":62,"repositoryHtmlUrl":63,"repositoryHomepageUrl":64,"ownerHtmlUrl":65,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":66,"install":67,"resources":80},"01M6JCNHG95NGFHHVFXFKPB8D7","api-design",null,"REST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.","S3YED\u002Fappie-kit","S3YED","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F62743867?v=4","01M6JBKBRRVP6KMS0VWZ27T3R6","01M6JBKBX0KG30284PW1DBYJT1",14,"MIT","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fecc\u002Fapi-design\u002FSKILL.md",[17,22,25,30],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":26,"categoryName":27,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",{"categoryNo":31,"categoryName":32,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000032","设计师",[34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52],"api design","api","design","pagination","for","rest api design","naming","api design patterns","codes","error","rest api","rate limiting","patterns","design patterns","filtering","versioning","REST","Production","status","2026-11-10 10:12:30",false,"---\nname: api-design\ndescription: REST API design patterns including resource naming, status codes, pagination, filtering, error responses, versioning, and rate limiting for production APIs.\norigin: ECC\n---\n\n# API Design Patterns\n\nConventions and best practices for designing consistent, developer-friendly REST APIs.\n\n## When to Activate\n\n- Designing new API endpoints\n- Reviewing existing API contracts\n- Adding pagination, filtering, or sorting\n- Implementing error handling for APIs\n- Planning API versioning strategy\n- Building public or partner-facing APIs\n\n## Resource Design\n\n### URL Structure\n\n```\n# Resources are nouns, plural, lowercase, kebab-case\nGET    \u002Fapi\u002Fv1\u002Fusers\nGET    \u002Fapi\u002Fv1\u002Fusers\u002F:id\nPOST   \u002Fapi\u002Fv1\u002Fusers\nPUT    \u002Fapi\u002Fv1\u002Fusers\u002F:id\nPATCH  \u002Fapi\u002Fv1\u002Fusers\u002F:id\nDELETE \u002Fapi\u002Fv1\u002Fusers\u002F:id\n\n# Sub-resources for relationships\nGET    \u002Fapi\u002Fv1\u002Fusers\u002F:id\u002Forders\nPOST   \u002Fapi\u002Fv1\u002Fusers\u002F:id\u002Forders\n\n# Actions that don't map to CRUD (use verbs sparingly)\nPOST   \u002Fapi\u002Fv1\u002Forders\u002F:id\u002Fcancel\nPOST   \u002Fapi\u002Fv1\u002Fauth\u002Flogin\nPOST   \u002Fapi\u002Fv1\u002Fauth\u002Frefresh\n```\n\n### Naming Rules\n\n```\n# GOOD\n\u002Fapi\u002Fv1\u002Fteam-members          # kebab-case for multi-word resources\n\u002Fapi\u002Fv1\u002Forders?status=active  # query params for filtering\n\u002Fapi\u002Fv1\u002Fusers\u002F123\u002Forders      # nested resources for ownership\n\n# BAD\n\u002Fapi\u002Fv1\u002FgetUsers              # verb in URL\n\u002Fapi\u002Fv1\u002Fuser                  # singular (use plural)\n\u002Fapi\u002Fv1\u002Fteam_members          # snake_case in URLs\n\u002Fapi\u002Fv1\u002Fusers\u002F123\u002FgetOrders   # verb in nested resource\n```\n\n## HTTP Methods and Status Codes\n\n### Method Semantics\n\n| Method | Idempotent | Safe | Use For |\n|--------|-----------|------|---------|\n| GET | Yes | Yes | Retrieve resources |\n| POST | No | No | Create resources, trigger actions |\n| PUT | Yes | No | Full replacement of a resource |\n| PATCH | No* | No | Partial update of a resource |\n| DELETE | Yes | No | Remove a resource |\n\n*PATCH can be made idempotent with proper implementation\n\n### Status Code Reference\n\n```\n# Success\n200 OK                    — GET, PUT, PATCH (with response body)\n201 Created               — POST (include Location header)\n204 No Content            — DELETE, PUT (no response body)\n\n# Client Errors\n400 Bad Request           — Validation failure, malformed JSON\n401 Unauthorized          — Missing or invalid authentication\n403 Forbidden             — Authenticated but not authorized\n404 Not Found             — Resource doesn't exist\n409 Conflict              — Duplicate entry, state conflict\n422 Unprocessable Entity  — Semantically invalid (valid JSON, bad data)\n429 Too Many Requests     — Rate limit exceeded\n\n# Server Errors\n500 Internal Server Error — Unexpected failure (never expose details)\n502 Bad Gateway           — Upstream service failed\n503 Service Unavailable   — Temporary overload, include Retry-After\n```\n\n### Common Mistakes\n\n```\n# BAD: 200 for everything\n{ \"status\": 200, \"success\": false, \"error\": \"Not found\" }\n\n# GOOD: Use HTTP status codes semantically\nHTTP\u002F1.1 404 Not Found\n{ \"error\": { \"code\": \"not_found\", \"message\": \"User not found\" } }\n\n# BAD: 500 for validation errors\n# GOOD: 400 or 422 with field-level details\n\n# BAD: 200 for created resources\n# GOOD: 201 with Location header\nHTTP\u002F1.1 201 Created\nLocation: \u002Fapi\u002Fv1\u002Fusers\u002Fabc-123\n```\n\n## Response Format\n\n### Success Response\n\n```json\n{\n  \"data\": {\n    \"id\": \"abc-123\",\n    \"email\": \"alice@example.com\",\n    \"name\": \"Alice\",\n    \"created_at\": \"2025-01-15T10:30:00Z\"\n  }\n}\n```\n\n### Collection Response (with Pagination)\n\n```json\n{\n  \"data\": [\n    { \"id\": \"abc-123\", \"name\": \"Alice\" },\n    { \"id\": \"def-456\", \"name\": \"Bob\" }\n  ],\n  \"meta\": {\n    \"total\": 142,\n    \"page\": 1,\n    \"per_page\": 20,\n    \"total_pages\": 8\n  },\n  \"links\": {\n    \"self\": \"\u002Fapi\u002Fv1\u002Fusers?page=1&per_page=20\",\n    \"next\": \"\u002Fapi\u002Fv1\u002Fusers?page=2&per_page=20\",\n    \"last\": \"\u002Fapi\u002Fv1\u002Fusers?page=8&per_page=20\"\n  }\n}\n```\n\n### Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"message\": \"Must be a valid email address\",\n        \"code\": \"invalid_format\"\n      },\n      {\n        \"field\": \"age\",\n        \"message\": \"Must be between 0 and 150\",\n        \"code\": \"out_of_range\"\n      }\n    ]\n  }\n}\n```\n\n### Response Envelope Variants\n\n```typescript\n\u002F\u002F Option A: Envelope with data wrapper (recommended for public APIs)\ninterface ApiResponse\u003CT> {\n  data: T;\n  meta?: PaginationMeta;\n  links?: PaginationLinks;\n}\n\ninterface ApiError {\n  error: {\n    code: string;\n    message: string;\n    details?: FieldError[];\n  };\n}\n\n\u002F\u002F Option B: Flat response (simpler, common for internal APIs)\n\u002F\u002F Success: just return the resource directly\n\u002F\u002F Error: return error object\n\u002F\u002F Distinguish by HTTP status code\n```\n\n## Pagination\n\n### Offset-Based (Simple)\n\n```\nGET \u002Fapi\u002Fv1\u002Fusers?page=2&per_page=20\n\n# Implementation\nSELECT * FROM users\nORDER BY created_at DESC\nLIMIT 20 OFFSET 20;\n```\n\n**Pros:** Easy to implement, supports \"jump to page N\"\n**Cons:** Slow on large offsets (OFFSET 100000), inconsistent with concurrent inserts\n\n### Cursor-Based (Scalable)\n\n```\nGET \u002Fapi\u002Fv1\u002Fusers?cursor=eyJpZCI6MTIzfQ&limit=20\n\n# Implementation\nSELECT * FROM users\nWHERE id > :cursor_id\nORDER BY id ASC\nLIMIT 21;  -- fetch one extra to determine has_next\n```\n\n```json\n{\n  \"data\": [...],\n  \"meta\": {\n    \"has_next\": true,\n    \"next_cursor\": \"eyJpZCI6MTQzfQ\"\n  }\n}\n```\n\n**Pros:** Consistent performance regardless of position, stable with concurrent inserts\n**Cons:** Cannot jump to arbitrary page, cursor is opaque\n\n### When to Use Which\n\n| Use Case | Pagination Type |\n|----------|----------------|\n| Admin dashboards, small datasets (\u003C10K) | Offset |\n| Infinite scroll, feeds, large datasets | Cursor |\n| Public APIs | Cursor (default) with offset (optional) |\n| Search results | Offset (users expect page numbers) |\n\n## Filtering, Sorting, and Search\n\n### Filtering\n\n```\n# Simple equality\nGET \u002Fapi\u002Fv1\u002Forders?status=active&customer_id=abc-123\n\n# Comparison operators (use bracket notation)\nGET \u002Fapi\u002Fv1\u002Fproducts?price[gte]=10&price[lte]=100\nGET \u002Fapi\u002Fv1\u002Forders?created_at[after]=2025-01-01\n\n# Multiple values (comma-separated)\nGET \u002Fapi\u002Fv1\u002Fproducts?category=electronics,clothing\n\n# Nested fields (dot notation)\nGET \u002Fapi\u002Fv1\u002Forders?customer.country=US\n```\n\n### Sorting\n\n```\n# Single field (prefix - for descending)\nGET \u002Fapi\u002Fv1\u002Fproducts?sort=-created_at\n\n# Multiple fields (comma-separated)\nGET \u002Fapi\u002Fv1\u002Fproducts?sort=-featured,price,-created_at\n```\n\n### Full-Text Search\n\n```\n# Search query parameter\nGET \u002Fapi\u002Fv1\u002Fproducts?q=wireless+headphones\n\n# Field-specific search\nGET \u002Fapi\u002Fv1\u002Fusers?email=alice\n```\n\n### Sparse Fieldsets\n\n```\n# Return only specified fields (reduces payload)\nGET \u002Fapi\u002Fv1\u002Fusers?fields=id,name,email\nGET \u002Fapi\u002Fv1\u002Forders?fields=id,total,status&include=customer.name\n```\n\n## Authentication and Authorization\n\n### Token-Based Auth\n\n```\n# Bearer token in Authorization header\nGET \u002Fapi\u002Fv1\u002Fusers\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...\n\n# API key (for server-to-server)\nGET \u002Fapi\u002Fv1\u002Fdata\nX-API-Key: \u003Capi-key>\n```\n\n### Authorization Patterns\n\n```typescript\n\u002F\u002F Resource-level: check ownership\napp.get(\"\u002Fapi\u002Fv1\u002Forders\u002F:id\", async (req, res) => {\n  const order = await Order.findById(req.params.id);\n  if (!order) return res.status(404).json({ error: { code: \"not_found\" } });\n  if (order.userId !== req.user.id) return res.status(403).json({ error: { code: \"forbidden\" } });\n  return res.json({ data: order });\n});\n\n\u002F\u002F Role-based: check permissions\napp.delete(\"\u002Fapi\u002Fv1\u002Fusers\u002F:id\", requireRole(\"admin\"), async (req, res) => {\n  await User.delete(req.params.id);\n  return res.status(204).send();\n});\n```\n\n## Rate Limiting\n\n### Headers\n\n```\nHTTP\u002F1.1 200 OK\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 95\nX-RateLimit-Reset: 1640000000\n\n# When exceeded\nHTTP\u002F1.1 429 Too Many Requests\nRetry-After: 60\n{\n  \"error\": {\n    \"code\": \"rate_limit_exceeded\",\n    \"message\": \"Rate limit exceeded. Try again in 60 seconds.\"\n  }\n}\n```\n\n### Rate Limit Tiers\n\n| Tier | Limit | Window | Use Case |\n|------|-------|--------|----------|\n| Anonymous | 30\u002Fmin | Per IP | Public endpoints |\n| Authenticated | 100\u002Fmin | Per user | Standard API access |\n| Premium | 1000\u002Fmin | Per API key | Paid API plans |\n| Internal | 10000\u002Fmin | Per service | Service-to-service |\n\n## Versioning\n\n### URL Path Versioning (Recommended)\n\n```\n\u002Fapi\u002Fv1\u002Fusers\n\u002Fapi\u002Fv2\u002Fusers\n```\n\n**Pros:** Explicit, easy to route, cacheable\n**Cons:** URL changes between versions\n\n### Header Versioning\n\n```\nGET \u002Fapi\u002Fusers\nAccept: application\u002Fvnd.myapp.v2+json\n```\n\n**Pros:** Clean URLs\n**Cons:** Harder to test, easy to forget\n\n### Versioning Strategy\n\n```\n1. Start with \u002Fapi\u002Fv1\u002F — don't version until you need to\n2. Maintain at most 2 active versions (current + previous)\n3. Deprecation timeline:\n   - Announce deprecation (6 months notice for public APIs)\n   - Add Sunset header: Sunset: Sat, 01 Jan 2026 00:00:00 GMT\n   - Return 410 Gone after sunset date\n4. Non-breaking changes don't need a new version:\n   - Adding new fields to responses\n   - Adding new optional query parameters\n   - Adding new endpoints\n5. Breaking changes require a new version:\n   - Removing or renaming fields\n   - Changing field types\n   - Changing URL structure\n   - Changing authentication method\n```\n\n## Implementation Patterns\n\n### TypeScript (Next.js API Route)\n\n```typescript\nimport { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next\u002Fserver\";\n\nconst createUserSchema = z.object({\n  email: z.string().email(),\n  name: z.string().min(1).max(100),\n});\n\nexport async function POST(req: NextRequest) {\n  const body = await req.json();\n  const parsed = createUserSchema.safeParse(body);\n\n  if (!parsed.success) {\n    return NextResponse.json({\n      error: {\n        code: \"validation_error\",\n        message: \"Request validation failed\",\n        details: parsed.error.issues.map(i => ({\n          field: i.path.join(\".\"),\n          message: i.message,\n          code: i.code,\n        })),\n      },\n    }, { status: 422 });\n  }\n\n  const user = await createUser(parsed.data);\n\n  return NextResponse.json(\n    { data: user },\n    {\n      status: 201,\n      headers: { Location: `\u002Fapi\u002Fv1\u002Fusers\u002F${user.id}` },\n    },\n  );\n}\n```\n\n### Python (Django REST Framework)\n\n```python\nfrom rest_framework import serializers, viewsets, status\nfrom rest_framework.response import Response\n\nclass CreateUserSerializer(serializers.Serializer):\n    email = serializers.EmailField()\n    name = serializers.CharField(max_length=100)\n\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = [\"id\", \"email\", \"name\", \"created_at\"]\n\nclass UserViewSet(viewsets.ModelViewSet):\n    serializer_class = UserSerializer\n    permission_classes = [IsAuthenticated]\n\n    def get_serializer_class(self):\n        if self.action == \"create\":\n            return CreateUserSerializer\n        return UserSerializer\n\n    def create(self, request):\n        serializer = CreateUserSerializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        user = UserService.create(**serializer.validated_data)\n        return Response(\n            {\"data\": UserSerializer(user).data},\n            status=status.HTTP_201_CREATED,\n            headers={\"Location\": f\"\u002Fapi\u002Fv1\u002Fusers\u002F{user.id}\"},\n        )\n```\n\n### Go (net\u002Fhttp)\n\n```go\nfunc (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {\n    var req CreateUserRequest\n    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n        writeError(w, http.StatusBadRequest, \"invalid_json\", \"Invalid request body\")\n        return\n    }\n\n    if err := req.Validate(); err != nil {\n        writeError(w, http.StatusUnprocessableEntity, \"validation_error\", err.Error())\n        return\n    }\n\n    user, err := h.service.Create(r.Context(), req)\n    if err != nil {\n        switch {\n        case errors.Is(err, domain.ErrEmailTaken):\n            writeError(w, http.StatusConflict, \"email_taken\", \"Email already registered\")\n        default:\n            writeError(w, http.StatusInternalServerError, \"internal_error\", \"Internal error\")\n        }\n        return\n    }\n\n    w.Header().Set(\"Location\", fmt.Sprintf(\"\u002Fapi\u002Fv1\u002Fusers\u002F%s\", user.ID))\n    writeJSON(w, http.StatusCreated, map[string]any{\"data\": user})\n}\n```\n\n## API Design Checklist\n\nBefore shipping a new endpoint:\n\n- [ ] Resource URL follows naming conventions (plural, kebab-case, no verbs)\n- [ ] Correct HTTP method used (GET for reads, POST for creates, etc.)\n- [ ] Appropriate status codes returned (not 200 for everything)\n- [ ] Input validated with schema (Zod, Pydantic, Bean Validation)\n- [ ] Error responses follow standard format with codes and messages\n- [ ] Pagination implemented for list endpoints (cursor or offset)\n- [ ] Authentication required (or explicitly marked as public)\n- [ ] Authorization checked (user can only access their own resources)\n- [ ] Rate limiting configured\n- [ ] Response does not leak internal details (stack traces, SQL errors)\n- [ ] Consistent naming with existing endpoints (camelCase vs snake_case)\n- [ ] Documented (OpenAPI\u002FSwagger spec updated)\n","# API Design Patterns\n\nConventions and best practices for designing consistent, developer-friendly REST APIs.\n\n## When to Activate\n\n- Designing new API endpoints\n- Reviewing existing API contracts\n- Adding pagination, filtering, or sorting\n- Implementing error handling for APIs\n- Planning API versioning strategy\n- Building public or partner-facing APIs\n\n## Resource Design\n\n### URL Structure\n\n```\n# Resources are nouns, plural, lowercase, kebab-case\nGET    \u002Fapi\u002Fv1\u002Fusers\nGET    \u002Fapi\u002Fv1\u002Fusers\u002F:id\nPOST   \u002Fapi\u002Fv1\u002Fusers\nPUT    \u002Fapi\u002Fv1\u002Fusers\u002F:id\nPATCH  \u002Fapi\u002Fv1\u002Fusers\u002F:id\nDELETE \u002Fapi\u002Fv1\u002Fusers\u002F:id\n\n# Sub-resources for relationships\nGET    \u002Fapi\u002Fv1\u002Fusers\u002F:id\u002Forders\nPOST   \u002Fapi\u002Fv1\u002Fusers\u002F:id\u002Forders\n\n# Actions that don't map to CRUD (use verbs sparingly)\nPOST   \u002Fapi\u002Fv1\u002Forders\u002F:id\u002Fcancel\nPOST   \u002Fapi\u002Fv1\u002Fauth\u002Flogin\nPOST   \u002Fapi\u002Fv1\u002Fauth\u002Frefresh\n```\n\n### Naming Rules\n\n```\n# GOOD\n\u002Fapi\u002Fv1\u002Fteam-members          # kebab-case for multi-word resources\n\u002Fapi\u002Fv1\u002Forders?status=active  # query params for filtering\n\u002Fapi\u002Fv1\u002Fusers\u002F123\u002Forders      # nested resources for ownership\n\n# BAD\n\u002Fapi\u002Fv1\u002FgetUsers              # verb in URL\n\u002Fapi\u002Fv1\u002Fuser                  # singular (use plural)\n\u002Fapi\u002Fv1\u002Fteam_members          # snake_case in URLs\n\u002Fapi\u002Fv1\u002Fusers\u002F123\u002FgetOrders   # verb in nested resource\n```\n\n## HTTP Methods and Status Codes\n\n### Method Semantics\n\n| Method | Idempotent | Safe | Use For |\n|--------|-----------|------|---------|\n| GET | Yes | Yes | Retrieve resources |\n| POST | No | No | Create resources, trigger actions |\n| PUT | Yes | No | Full replacement of a resource |\n| PATCH | No* | No | Partial update of a resource |\n| DELETE | Yes | No | Remove a resource |\n\n*PATCH can be made idempotent with proper implementation\n\n### Status Code Reference\n\n```\n# Success\n200 OK                    — GET, PUT, PATCH (with response body)\n201 Created               — POST (include Location header)\n204 No Content            — DELETE, PUT (no response body)\n\n# Client Errors\n400 Bad Request           — Validation failure, malformed JSON\n401 Unauthorized          — Missing or invalid authentication\n403 Forbidden             — Authenticated but not authorized\n404 Not Found             — Resource doesn't exist\n409 Conflict              — Duplicate entry, state conflict\n422 Unprocessable Entity  — Semantically invalid (valid JSON, bad data)\n429 Too Many Requests     — Rate limit exceeded\n\n# Server Errors\n500 Internal Server Error — Unexpected failure (never expose details)\n502 Bad Gateway           — Upstream service failed\n503 Service Unavailable   — Temporary overload, include Retry-After\n```\n\n### Common Mistakes\n\n```\n# BAD: 200 for everything\n{ \"status\": 200, \"success\": false, \"error\": \"Not found\" }\n\n# GOOD: Use HTTP status codes semantically\nHTTP\u002F1.1 404 Not Found\n{ \"error\": { \"code\": \"not_found\", \"message\": \"User not found\" } }\n\n# BAD: 500 for validation errors\n# GOOD: 400 or 422 with field-level details\n\n# BAD: 200 for created resources\n# GOOD: 201 with Location header\nHTTP\u002F1.1 201 Created\nLocation: \u002Fapi\u002Fv1\u002Fusers\u002Fabc-123\n```\n\n## Response Format\n\n### Success Response\n\n```json\n{\n  \"data\": {\n    \"id\": \"abc-123\",\n    \"email\": \"alice@example.com\",\n    \"name\": \"Alice\",\n    \"created_at\": \"2025-01-15T10:30:00Z\"\n  }\n}\n```\n\n### Collection Response (with Pagination)\n\n```json\n{\n  \"data\": [\n    { \"id\": \"abc-123\", \"name\": \"Alice\" },\n    { \"id\": \"def-456\", \"name\": \"Bob\" }\n  ],\n  \"meta\": {\n    \"total\": 142,\n    \"page\": 1,\n    \"per_page\": 20,\n    \"total_pages\": 8\n  },\n  \"links\": {\n    \"self\": \"\u002Fapi\u002Fv1\u002Fusers?page=1&per_page=20\",\n    \"next\": \"\u002Fapi\u002Fv1\u002Fusers?page=2&per_page=20\",\n    \"last\": \"\u002Fapi\u002Fv1\u002Fusers?page=8&per_page=20\"\n  }\n}\n```\n\n### Error Response\n\n```json\n{\n  \"error\": {\n    \"code\": \"validation_error\",\n    \"message\": \"Request validation failed\",\n    \"details\": [\n      {\n        \"field\": \"email\",\n        \"message\": \"Must be a valid email address\",\n        \"code\": \"invalid_format\"\n      },\n      {\n        \"field\": \"age\",\n        \"message\": \"Must be between 0 and 150\",\n        \"code\": \"out_of_range\"\n      }\n    ]\n  }\n}\n```\n\n### Response Envelope Variants\n\n```typescript\n\u002F\u002F Option A: Envelope with data wrapper (recommended for public APIs)\ninterface ApiResponse\u003CT> {\n  data: T;\n  meta?: PaginationMeta;\n  links?: PaginationLinks;\n}\n\ninterface ApiError {\n  error: {\n    code: string;\n    message: string;\n    details?: FieldError[];\n  };\n}\n\n\u002F\u002F Option B: Flat response (simpler, common for internal APIs)\n\u002F\u002F Success: just return the resource directly\n\u002F\u002F Error: return error object\n\u002F\u002F Distinguish by HTTP status code\n```\n\n## Pagination\n\n### Offset-Based (Simple)\n\n```\nGET \u002Fapi\u002Fv1\u002Fusers?page=2&per_page=20\n\n# Implementation\nSELECT * FROM users\nORDER BY created_at DESC\nLIMIT 20 OFFSET 20;\n```\n\n**Pros:** Easy to implement, supports \"jump to page N\"\n**Cons:** Slow on large offsets (OFFSET 100000), inconsistent with concurrent inserts\n\n### Cursor-Based (Scalable)\n\n```\nGET \u002Fapi\u002Fv1\u002Fusers?cursor=eyJpZCI6MTIzfQ&limit=20\n\n# Implementation\nSELECT * FROM users\nWHERE id > :cursor_id\nORDER BY id ASC\nLIMIT 21;  -- fetch one extra to determine has_next\n```\n\n```json\n{\n  \"data\": [...],\n  \"meta\": {\n    \"has_next\": true,\n    \"next_cursor\": \"eyJpZCI6MTQzfQ\"\n  }\n}\n```\n\n**Pros:** Consistent performance regardless of position, stable with concurrent inserts\n**Cons:** Cannot jump to arbitrary page, cursor is opaque\n\n### When to Use Which\n\n| Use Case | Pagination Type |\n|----------|----------------|\n| Admin dashboards, small datasets (\u003C10K) | Offset |\n| Infinite scroll, feeds, large datasets | Cursor |\n| Public APIs | Cursor (default) with offset (optional) |\n| Search results | Offset (users expect page numbers) |\n\n## Filtering, Sorting, and Search\n\n### Filtering\n\n```\n# Simple equality\nGET \u002Fapi\u002Fv1\u002Forders?status=active&customer_id=abc-123\n\n# Comparison operators (use bracket notation)\nGET \u002Fapi\u002Fv1\u002Fproducts?price[gte]=10&price[lte]=100\nGET \u002Fapi\u002Fv1\u002Forders?created_at[after]=2025-01-01\n\n# Multiple values (comma-separated)\nGET \u002Fapi\u002Fv1\u002Fproducts?category=electronics,clothing\n\n# Nested fields (dot notation)\nGET \u002Fapi\u002Fv1\u002Forders?customer.country=US\n```\n\n### Sorting\n\n```\n# Single field (prefix - for descending)\nGET \u002Fapi\u002Fv1\u002Fproducts?sort=-created_at\n\n# Multiple fields (comma-separated)\nGET \u002Fapi\u002Fv1\u002Fproducts?sort=-featured,price,-created_at\n```\n\n### Full-Text Search\n\n```\n# Search query parameter\nGET \u002Fapi\u002Fv1\u002Fproducts?q=wireless+headphones\n\n# Field-specific search\nGET \u002Fapi\u002Fv1\u002Fusers?email=alice\n```\n\n### Sparse Fieldsets\n\n```\n# Return only specified fields (reduces payload)\nGET \u002Fapi\u002Fv1\u002Fusers?fields=id,name,email\nGET \u002Fapi\u002Fv1\u002Forders?fields=id,total,status&include=customer.name\n```\n\n## Authentication and Authorization\n\n### Token-Based Auth\n\n```\n# Bearer token in Authorization header\nGET \u002Fapi\u002Fv1\u002Fusers\nAuthorization: Bearer eyJhbGciOiJIUzI1NiIs...\n\n# API key (for server-to-server)\nGET \u002Fapi\u002Fv1\u002Fdata\nX-API-Key: \u003Capi-key>\n```\n\n### Authorization Patterns\n\n```typescript\n\u002F\u002F Resource-level: check ownership\napp.get(\"\u002Fapi\u002Fv1\u002Forders\u002F:id\", async (req, res) => {\n  const order = await Order.findById(req.params.id);\n  if (!order) return res.status(404).json({ error: { code: \"not_found\" } });\n  if (order.userId !== req.user.id) return res.status(403).json({ error: { code: \"forbidden\" } });\n  return res.json({ data: order });\n});\n\n\u002F\u002F Role-based: check permissions\napp.delete(\"\u002Fapi\u002Fv1\u002Fusers\u002F:id\", requireRole(\"admin\"), async (req, res) => {\n  await User.delete(req.params.id);\n  return res.status(204).send();\n});\n```\n\n## Rate Limiting\n\n### Headers\n\n```\nHTTP\u002F1.1 200 OK\nX-RateLimit-Limit: 100\nX-RateLimit-Remaining: 95\nX-RateLimit-Reset: 1640000000\n\n# When exceeded\nHTTP\u002F1.1 429 Too Many Requests\nRetry-After: 60\n{\n  \"error\": {\n    \"code\": \"rate_limit_exceeded\",\n    \"message\": \"Rate limit exceeded. Try again in 60 seconds.\"\n  }\n}\n```\n\n### Rate Limit Tiers\n\n| Tier | Limit | Window | Use Case |\n|------|-------|--------|----------|\n| Anonymous | 30\u002Fmin | Per IP | Public endpoints |\n| Authenticated | 100\u002Fmin | Per user | Standard API access |\n| Premium | 1000\u002Fmin | Per API key | Paid API plans |\n| Internal | 10000\u002Fmin | Per service | Service-to-service |\n\n## Versioning\n\n### URL Path Versioning (Recommended)\n\n```\n\u002Fapi\u002Fv1\u002Fusers\n\u002Fapi\u002Fv2\u002Fusers\n```\n\n**Pros:** Explicit, easy to route, cacheable\n**Cons:** URL changes between versions\n\n### Header Versioning\n\n```\nGET \u002Fapi\u002Fusers\nAccept: application\u002Fvnd.myapp.v2+json\n```\n\n**Pros:** Clean URLs\n**Cons:** Harder to test, easy to forget\n\n### Versioning Strategy\n\n```\n1. Start with \u002Fapi\u002Fv1\u002F — don't version until you need to\n2. Maintain at most 2 active versions (current + previous)\n3. Deprecation timeline:\n   - Announce deprecation (6 months notice for public APIs)\n   - Add Sunset header: Sunset: Sat, 01 Jan 2026 00:00:00 GMT\n   - Return 410 Gone after sunset date\n4. Non-breaking changes don't need a new version:\n   - Adding new fields to responses\n   - Adding new optional query parameters\n   - Adding new endpoints\n5. Breaking changes require a new version:\n   - Removing or renaming fields\n   - Changing field types\n   - Changing URL structure\n   - Changing authentication method\n```\n\n## Implementation Patterns\n\n### TypeScript (Next.js API Route)\n\n```typescript\nimport { z } from \"zod\";\nimport { NextRequest, NextResponse } from \"next\u002Fserver\";\n\nconst createUserSchema = z.object({\n  email: z.string().email(),\n  name: z.string().min(1).max(100),\n});\n\nexport async function POST(req: NextRequest) {\n  const body = await req.json();\n  const parsed = createUserSchema.safeParse(body);\n\n  if (!parsed.success) {\n    return NextResponse.json({\n      error: {\n        code: \"validation_error\",\n        message: \"Request validation failed\",\n        details: parsed.error.issues.map(i => ({\n          field: i.path.join(\".\"),\n          message: i.message,\n          code: i.code,\n        })),\n      },\n    }, { status: 422 });\n  }\n\n  const user = await createUser(parsed.data);\n\n  return NextResponse.json(\n    { data: user },\n    {\n      status: 201,\n      headers: { Location: `\u002Fapi\u002Fv1\u002Fusers\u002F${user.id}` },\n    },\n  );\n}\n```\n\n### Python (Django REST Framework)\n\n```python\nfrom rest_framework import serializers, viewsets, status\nfrom rest_framework.response import Response\n\nclass CreateUserSerializer(serializers.Serializer):\n    email = serializers.EmailField()\n    name = serializers.CharField(max_length=100)\n\nclass UserSerializer(serializers.ModelSerializer):\n    class Meta:\n        model = User\n        fields = [\"id\", \"email\", \"name\", \"created_at\"]\n\nclass UserViewSet(viewsets.ModelViewSet):\n    serializer_class = UserSerializer\n    permission_classes = [IsAuthenticated]\n\n    def get_serializer_class(self):\n        if self.action == \"create\":\n            return CreateUserSerializer\n        return UserSerializer\n\n    def create(self, request):\n        serializer = CreateUserSerializer(data=request.data)\n        serializer.is_valid(raise_exception=True)\n        user = UserService.create(**serializer.validated_data)\n        return Response(\n            {\"data\": UserSerializer(user).data},\n            status=status.HTTP_201_CREATED,\n            headers={\"Location\": f\"\u002Fapi\u002Fv1\u002Fusers\u002F{user.id}\"},\n        )\n```\n\n### Go (net\u002Fhttp)\n\n```go\nfunc (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {\n    var req CreateUserRequest\n    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n        writeError(w, http.StatusBadRequest, \"invalid_json\", \"Invalid request body\")\n        return\n    }\n\n    if err := req.Validate(); err != nil {\n        writeError(w, http.StatusUnprocessableEntity, \"validation_error\", err.Error())\n        return\n    }\n\n    user, err := h.service.Create(r.Context(), req)\n    if err != nil {\n        switch {\n        case errors.Is(err, domain.ErrEmailTaken):\n            writeError(w, http.StatusConflict, \"email_taken\", \"Email already registered\")\n        default:\n            writeError(w, http.StatusInternalServerError, \"internal_error\", \"Internal error\")\n        }\n        return\n    }\n\n    w.Header().Set(\"Location\", fmt.Sprintf(\"\u002Fapi\u002Fv1\u002Fusers\u002F%s\", user.ID))\n    writeJSON(w, http.StatusCreated, map[string]any{\"data\": user})\n}\n```\n\n## API Design Checklist\n\nBefore shipping a new endpoint:\n\n- [ ] Resource URL follows naming conventions (plural, kebab-case, no verbs)\n- [ ] Correct HTTP method used (GET for reads, POST for creates, etc.)\n- [ ] Appropriate status codes returned (not 200 for everything)\n- [ ] Input validated with schema (Zod, Pydantic, Bean Validation)\n- [ ] Error responses follow standard format with codes and messages\n- [ ] Pagination implemented for list endpoints (cursor or offset)\n- [ ] Authentication required (or explicitly marked as public)\n- [ ] Authorization checked (user can only access their own resources)\n- [ ] Rate limiting configured\n- [ ] Response does not leak internal details (stack traces, SQL errors)\n- [ ] Consistent naming with existing endpoints (camelCase vs snake_case)\n- [ ] Documented (OpenAPI\u002FSwagger spec updated)\n",{"name":5,"origin":58,"description":7},"ECC","master","8ec37df6d71fb664ea19d32c079e7d3ff931a8e0","skills\u002Fecc\u002Fapi-design",3,"https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit","https:\u002F\u002Fweblyfe.ai","https:\u002F\u002Fgithub.com\u002FS3YED","2026-08-27 01:54:02",{"source":8,"skillName":5,"commands":68,"prompt":78,"zipAvailable":79},[69,72,75],{"tool":70,"command":71},"npx","npx skills add S3YED\u002Fappie-kit --skill api-design",{"tool":73,"command":74},"bunx","bunx skills add S3YED\u002Fappie-kit --skill api-design",{"tool":76,"command":77},"pnpm","pnpm dlx skills add S3YED\u002Fappie-kit --skill api-design","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fecc\u002Fapi-design\u002FSKILL.md",true,[],[82,116,144,170,198,222],{"skillNo":83,"name":84,"nameZh":6,"description":85,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":97,"publishedAt":53},"01M6JFD4M6N4DSGC7TXEJ5R3J2","ai-music","AI music generation and audio tools: HeartMuLa lyrics-to-music generation, songwriting craft (+ Suno prompts), and audio spectrogram\u002Ffeature visualization.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fwethlete\u002Fai-music\u002FSKILL.md",[88,91,94],{"categoryNo":89,"categoryName":90,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":92,"categoryName":93,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",[98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115],"music","ai music","humanizer","visualization","Tools","Audio","hermes","hermes agent","songwriting","suno","lyrics","prompts","heartmula","craft","generation","feature","ai music generation","music generation",{"skillNo":117,"name":118,"nameZh":6,"description":119,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":120,"categories":121,"keywords":124,"publishedAt":53},"01M6JFCZE4XNFQFMNZDHKHT5V5","agentic-webdesign-design-system-ingestion","Use v0 Design Systems 2.0, Bolt Design System Agents, or Lovable to generate production UI from a client's real design system components — not generic Tailwind. Use when building a client website where brand consistency matters.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-design-system-ingestion\u002FSKILL.md",[122,123],{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":31,"categoryName":32,"taxonomyType":28,"taxonomyTypeDesc":29},[36,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143],"agentic","design system","system","ingestion","webdesign","agentic webdesign design system ingestion","components","generate","design systems","website","ui","Tailwind","bolt","lovable","v0","design-system","wed","design system components","appie",{"skillNo":145,"name":146,"nameZh":6,"description":147,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":148,"categories":149,"keywords":152,"publishedAt":53},"01M6JFCVPX3913CFAS6QQJ33ZB","agentic-webdesign-agent-elements","Use 21st.dev Agent Elements — 25 shadcn\u002Fui components purpose-built for AI agent UIs. Tool cards (Bash, Edit, Search, Plan, Subagent, MCP), chat, input, streaming. Open source, shadcn-registry install, no runtime lock-in.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fweb-design\u002Fagentic-webdesign-agent-elements\u002FSKILL.md",[150,151],{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":31,"categoryName":32,"taxonomyType":28,"taxonomyTypeDesc":29},[125,153,129,154,155,156,131,157,158,159,160,161,162,163,164,165,166,167,168,169],"elements","agentic webdesign agent elements","MCP","ai agent","registry","search","streaming","open source","open","Bash","plan","subagent","edit","install","input","lock","source",{"skillNo":171,"name":172,"nameZh":6,"description":173,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":174,"categories":175,"keywords":180,"publishedAt":53},"01M6JFCQC7H65M9QJ6TRE71BS1","vercel-deployment","Deploy static sites and frontends to Vercel — handles both CLI and REST API paths, including the CLI token sanitization workaround.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fvercel-deployment\u002FSKILL.md",[176,179],{"categoryNo":177,"categoryName":178,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000011","运维与部署",{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},[181,182,183,184,185,186,187,188,189,50,35,190,191,192,44,193,194,195,196,197],"deployment","vercel deployment","vercel","macos","cli","linux","windows","deploy","vercel deploy","site","deploy to vercel","token","host","sites","paths","sanitization","static site",{"skillNo":199,"name":200,"nameZh":6,"description":201,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":202,"categories":203,"keywords":205,"publishedAt":53},"01M6JFCM4SMTXBM5A01BK2YQG1","software-quality-workflows","Umbrella workflow for TDD, debugging, code simplification, spikes, pre-commit review, and debugger-assisted investigation.","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Fsoftware-quality-workflows\u002FSKILL.md",[204],{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},[206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,38],"workflows","quality","software","software quality workflows","review","tdd","workflow","commit","debugging","code","debugger","pre-commit","investigation","code simplification","user","simplification",{"skillNo":223,"name":224,"nameZh":6,"description":225,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":226,"categories":227,"keywords":235,"publishedAt":53},"01M6JFCGD0M6CEATY01W7YN6DB","react-component-refactoring","Safely refactor large React components into smaller, maintainable pieces using proven patterns","https:\u002F\u002Fgithub.com\u002FS3YED\u002Fappie-kit\u002Fblob\u002Fmaster\u002Fskills\u002Fsoftware-development\u002Freact-component-refactoring\u002FSKILL.md",[228,231,234],{"categoryNo":229,"categoryName":230,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":232,"categoryName":233,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000009","前端开发",{"categoryNo":95,"categoryName":96,"taxonomyType":20,"taxonomyTypeDesc":21},[236,237,238,239,240,241,242,243,184,244,245,131,246,187,247,215,248,249,46,250],"react","component","refactoring","component refactoring","react component","react component refactoring","frontend","writing","safe","typescript","react components","modular","refactor","simplify","test-driven-development"]