API Designer
Purpose
Provides expert REST and GraphQL API architecture expertise specializing in OpenAPI 3.1 specifications, API versioning strategies, pagination patterns, and hypermedia-driven design (HATEOAS). Focuses on building scalable, well-documented, developer-friendly APIs with proper error handling and standardization.
When to Use
-
Designing RESTful or GraphQL APIs from requirements
-
Creating OpenAPI 3.1 specifications for API documentation
-
Implementing API versioning strategies (URL, header, content negotiation)
-
Designing pagination, filtering, and sorting patterns for large datasets
-
Building HATEOAS-compliant APIs (hypermedia-driven)
-
Standardizing error responses and status codes across services
-
Designing API authentication and authorization patterns
Quick Start
Invoke this skill when:
-
Designing RESTful or GraphQL APIs from requirements
-
Creating OpenAPI 3.1 specifications for API documentation
-
Implementing API versioning strategies (URL, header, content negotiation)
-
Designing pagination, filtering, and sorting patterns for large datasets
-
Building HATEOAS-compliant APIs (hypermedia-driven)
-
Standardizing error responses and status codes across services
Do NOT invoke when:
-
Only implementing pre-designed API endpoints (use backend-developer)
-
Database schema design without API context (use database-administrator)
-
Frontend API integration (use frontend-developer)
-
API security implementation (use security-engineer for authentication/authorization)
-
API performance optimization (use performance-engineer)
Core Workflows
Workflow 1: Design RESTful API with OpenAPI 3.1
Use case: E-commerce platform needs product catalog API
Step 1: Resource Modeling
Resources identified:
- Products (CRUD)
- Categories (read-only, hierarchical)
- Reviews (nested under products)
- Inventory (separate resource, linked to products)
URL Structure Design:
GET /v1/products # List products (paginated) POST /v1/products # Create product GET /v1/products/{id} # Get product details PUT /v1/products/{id} # Update product (full replacement) PATCH /v1/products/{id} # Partial update DELETE /v1/products/{id} # Delete product
GET /v1/products/{id}/reviews # Get reviews for product POST /v1/products/{id}/reviews # Create review GET /v1/products/{id}/reviews/{reviewId} # Get specific review
GET /v1/categories # List categories GET /v1/categories/{id} # Get category + subcategories
Query parameters (filtering, pagination, sorting):
GET /v1/products?category=electronics&min_price=100&max_price=500&sort=price:asc&limit=20&cursor=abc123
Step 2: OpenAPI 3.1 Specification
openapi.yaml
openapi: 3.1.0 info: title: E-commerce Product API version: 1.0.0 description: RESTful API for product catalog management contact: name: API Support email: api@ecommerce.com
servers:
- url: https://api.ecommerce.com/v1 description: Production server
- url: https://staging-api.ecommerce.com/v1 description: Staging server
paths: /products: get: summary: List products operationId: listProducts tags: [Products] parameters: - name: category in: query description: Filter by category slug schema: type: string example: electronics - name: min_price in: query description: Minimum price filter schema: type: number format: float minimum: 0 - name: max_price in: query description: Maximum price filter schema: type: number format: float minimum: 0 - name: sort in: query description: Sort order (field:direction) schema: type: string enum: [price:asc, price:desc, created_at:asc, created_at:desc] default: created_at:desc - name: limit in: query description: Number of results per page schema: type: integer minimum: 1 maximum: 100 default: 20 - name: cursor in: query description: Pagination cursor (opaque token) schema: type: string responses: '200': description: Successful response content: application/json: schema: type: object required: [data, meta, links] properties: data: type: array items: $ref: '#/components/schemas/Product' meta: type: object properties: total_count: type: integer description: Total number of products matching filters has_more: type: boolean description: Whether more results exist links: type: object properties: self: type: string format: uri next: type: string format: uri nullable: true prev: type: string format: uri nullable: true examples: success: value: data: - id: "prod_123" name: "Wireless Headphones" description: "Premium noise-cancelling headphones" price: 299.99 currency: "USD" category: id: "cat_1" name: "Electronics" created_at: "2024-01-15T10:30:00Z" meta: total_count: 1523 has_more: true links: self: "/v1/products?limit=20" next: "/v1/products?limit=20&cursor=eyJpZCI6InByb2RfMTIzIn0=" prev: null '400': $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError'
/products/{id}: get: summary: Get product details operationId: getProduct tags: [Products] parameters: - name: id in: path required: true description: Product ID schema: type: string pattern: '^prod_[a-zA-Z0-9]+$' responses: '200': description: Product found content: application/json: schema: $ref: '#/components/schemas/Product' '404': $ref: '#/components/responses/NotFound'
components: schemas: Product: type: object required: [id, name, price, currency] properties: id: type: string description: Unique product identifier example: "prod_123" name: type: string minLength: 1 maxLength: 200 example: "Wireless Headphones" description: type: string maxLength: 2000 nullable: true price: type: number format: float minimum: 0 example: 299.99 currency: type: string enum: [USD, EUR, GBP, JPY] default: USD category: $ref: '#/components/schemas/Category' images: type: array items: type: string format: uri maxItems: 10 inventory_count: type: integer minimum: 0 description: Available stock created_at: type: string format: date-time updated_at: type: string format: date-time
Category:
type: object
required: [id, name, slug]
properties:
id:
type: string
example: "cat_1"
name:
type: string
example: "Electronics"
slug:
type: string
pattern: '^[a-z0-9-]+$'
example: "electronics"
parent_id:
type: string
nullable: true
Error:
type: object
required: [error]
properties:
error:
type: object
required: [code, message]
properties:
code:
type: string
description: Machine-readable error code
example: "invalid_parameter"
message:
type: string
description: Human-readable error message
example: "The 'price' parameter must be a positive number"
details:
type: object
description: Additional error context
additionalProperties: true
responses: BadRequest: description: Invalid request parameters content: application/json: schema: $ref: '#/components/schemas/Error' example: error: code: "invalid_parameter" message: "The 'min_price' parameter must be a non-negative number" details: parameter: "min_price" value: "-10"
NotFound:
description: Resource not found
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: "resource_not_found"
message: "Product with ID 'prod_999' not found"
InternalServerError:
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
example:
error:
code: "internal_server_error"
message: "An unexpected error occurred. Please try again later."
details:
request_id: "req_abc123"
securitySchemes: ApiKey: type: apiKey in: header name: X-API-Key BearerAuth: type: http scheme: bearer bearerFormat: JWT
security:
- ApiKey: []
- BearerAuth: []
Step 3: Generate Documentation
Install Redoc CLI
npm install -g redoc-cli
Generate static HTML documentation
redoc-cli bundle openapi.yaml -o api-docs.html
Host documentation
npx serve api-docs.html
Interactive Swagger UI
docker run -p 8080:8080 -e SWAGGER_JSON=/docs/openapi.yaml
-v $(pwd):/docs swaggerapi/swagger-ui
Open http://localhost:8080 for interactive API testing
Anti-Patterns & Gotchas
❌ Anti-Pattern 1: Inconsistent Error Responses
What it looks like:
// Endpoint 1: Login failure { "error": "Invalid credentials" }
// Endpoint 2: Validation failure { "errors": [ { "field": "email", "message": "Invalid email format" } ] }
// Endpoint 3: Server error { "status": "error", "message": "Internal server error", "code": 500 }
// Problem: Clients need custom error handling per endpoint
Why it fails:
-
Client code becomes complex (multiple error parsing strategies)
-
Frontend developers frustrated (inconsistent contracts)
-
Error logging/monitoring difficult (no standard format)
Correct approach:
// Standardized error response (all endpoints) { "error": { "code": "invalid_credentials", "message": "The provided email or password is incorrect", "details": null, "request_id": "req_abc123" } }
// Validation errors (multiple fields) { "error": { "code": "validation_failed", "message": "One or more fields failed validation", "details": { "fields": [ { "field": "email", "message": "Invalid email format" }, { "field": "password", "message": "Password must be at least 8 characters" } ] }, "request_id": "req_def456" } }
// Client-side error handling (consistent) function handleApiError(response) { const { code, message, details } = response.error;
switch (code) { case 'validation_failed': // Display field-specific errors details.fields.forEach(({ field, message }) => { showFieldError(field, message); }); break;
case 'unauthorized':
// Redirect to login
redirectToLogin();
break;
default:
// Generic error message
showToast(message);
} }
Integration Patterns
backend-developer:
-
Handoff: API designer creates spec → Backend implements endpoints
-
Collaboration: Error response formats, authentication patterns
-
Tools: OpenAPI code generation, API mocking
frontend-developer:
-
Handoff: API spec published → Frontend consumes API
-
Collaboration: Query patterns, pagination, error handling
-
Tools: TypeScript type generation from OpenAPI/GraphQL schema
security-engineer:
-
Handoff: API designer defines authentication needs → Security implements auth
-
Collaboration: Rate limiting, API key management, OAuth flows
-
Critical: JWT validation, API gateway security policies
devops-engineer:
-
Handoff: API design finalized → DevOps deploys API gateway
-
Collaboration: API versioning deployment, blue-green releases
-
Tools: Kong, AWS API Gateway, Traefik configuration