Awesome-omni-skill api-design
REST API, GraphQL, and API design patterns and best practices
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/api-design-enact-on" ~/.claude/skills/diegosouzapw-awesome-omni-skill-api-design-c7dae4 && rm -rf "$T"
manifest:
skills/development/api-design-enact-on/SKILL.mdsource content
API Design Skill
Comprehensive patterns and best practices for designing REST APIs, GraphQL APIs, and API-first development.
What I Know
REST API Design Principles
Resource Naming
# Good - nouns, plural, lowercase, hyphenated GET /users GET /users/{id} GET /users/{id}/posts GET /blog-posts GET /order-items # Bad - verbs, singular, camelCase GET /getUsers GET /user GET /getUserPosts GET /blogPosts
HTTP Methods
| Method | Usage | Idempotent | Request Body | Response |
|---|---|---|---|---|
| GET | Read resource(s) | Yes | No | Resource(s) |
| POST | Create resource | No | Yes | Created resource |
| PUT | Replace resource | Yes | Yes | Updated resource |
| PATCH | Partial update | No* | Yes | Updated resource |
| DELETE | Remove resource | Yes | No | Empty or confirmation |
Status Codes
# Success 200 OK - Successful GET, PUT, PATCH 201 Created - Successful POST 204 No Content - Successful DELETE # Client Errors 400 Bad Request - Invalid request body/params 401 Unauthorized - Missing/invalid authentication 403 Forbidden - Valid auth, insufficient permissions 404 Not Found - Resource doesn't exist 409 Conflict - Resource state conflict (duplicate) 422 Unprocessable - Validation errors # Server Errors 500 Internal Error - Unexpected server error 502 Bad Gateway - Upstream service error 503 Service Unavailable - Temporary overload
Standardized Response Format
Success Response
{ "success": true, "data": { "id": 123, "email": "user@example.com", "name": "John Doe" } }
List Response with Pagination
{ "success": true, "data": [ { "id": 1, "name": "Item 1" }, { "id": 2, "name": "Item 2" } ], "meta": { "page": 1, "limit": 20, "total": 100, "totalPages": 5, "hasMore": true } }
Error Response
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Validation failed", "details": [ { "field": "email", "message": "Invalid email format" }, { "field": "password", "message": "Password must be at least 8 characters" } ] } }
Pagination Patterns
Offset-Based (Simple)
GET /users?page=2&limit=20 Response: { "data": [...], "meta": { "page": 2, "limit": 20, "total": 150, "totalPages": 8 } }
Cursor-Based (Scalable)
GET /users?cursor=abc123&limit=20 Response: { "data": [...], "meta": { "limit": 20, "nextCursor": "def456", "hasMore": true } }
Filtering & Sorting
Query Parameters
# Filtering GET /users?status=active GET /users?role=admin&status=active GET /products?price_min=10&price_max=100 GET /posts?created_after=2024-01-01 # Sorting GET /users?sort=name # Ascending GET /users?sort=-createdAt # Descending GET /users?sort=name,-createdAt # Multiple # Field Selection GET /users?fields=id,name,email # Search GET /users?search=john GET /products?q=laptop
API Versioning
URI Versioning (Recommended)
GET /api/v1/users GET /api/v2/users
Header Versioning
GET /api/users Accept: application/vnd.api+json;version=1
Query Parameter
GET /api/users?version=1
Authentication Patterns
JWT Bearer Token
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
API Key
# Header X-API-Key: your-api-key # Query (less secure) GET /api/users?api_key=your-api-key
OAuth 2.0 Flows
# Authorization Code (Web apps) # Client Credentials (Server-to-server) # PKCE (Mobile/SPA)
Rate Limiting
Headers
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1704067200 Retry-After: 60
429 Response
{ "success": false, "error": { "code": "RATE_LIMIT_EXCEEDED", "message": "Too many requests", "retryAfter": 60 } }
OpenAPI/Swagger Specification
openapi: 3.0.3 info: title: My API version: 1.0.0 description: API for managing users servers: - url: https://api.example.com/v1 description: Production paths: /users: get: summary: List users tags: - Users parameters: - name: page in: query schema: type: integer default: 1 - name: limit in: query schema: type: integer default: 20 maximum: 100 responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/UserList' '401': $ref: '#/components/responses/Unauthorized' post: summary: Create user tags: - Users requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUser' responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/UserResponse' '400': $ref: '#/components/responses/BadRequest' '409': $ref: '#/components/responses/Conflict' /users/{id}: get: summary: Get user by ID tags: - Users parameters: - name: id in: path required: true schema: type: integer responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/UserResponse' '404': $ref: '#/components/responses/NotFound' components: schemas: User: type: object properties: id: type: integer email: type: string format: email name: type: string createdAt: type: string format: date-time required: - id - email - name CreateUser: type: object properties: email: type: string format: email name: type: string minLength: 2 maxLength: 100 password: type: string minLength: 8 required: - email - name - password UserResponse: type: object properties: success: type: boolean data: $ref: '#/components/schemas/User' UserList: type: object properties: success: type: boolean data: type: array items: $ref: '#/components/schemas/User' meta: $ref: '#/components/schemas/Pagination' Pagination: type: object properties: page: type: integer limit: type: integer total: type: integer totalPages: type: integer Error: type: object properties: success: type: boolean example: false error: type: object properties: code: type: string message: type: string details: type: array items: type: object responses: BadRequest: description: Bad Request content: application/json: schema: $ref: '#/components/schemas/Error' Unauthorized: description: Unauthorized content: application/json: schema: $ref: '#/components/schemas/Error' NotFound: description: Not Found content: application/json: schema: $ref: '#/components/schemas/Error' Conflict: description: Conflict content: application/json: schema: $ref: '#/components/schemas/Error' securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT security: - bearerAuth: []
GraphQL Patterns
Schema Design
type Query { user(id: ID!): User users( page: Int = 1 limit: Int = 20 filter: UserFilter ): UserConnection! } type Mutation { createUser(input: CreateUserInput!): UserPayload! updateUser(id: ID!, input: UpdateUserInput!): UserPayload! deleteUser(id: ID!): DeletePayload! } type User { id: ID! email: String! name: String! posts(first: Int): [Post!]! createdAt: DateTime! } input CreateUserInput { email: String! name: String! password: String! } input UpdateUserInput { email: String name: String } input UserFilter { status: UserStatus role: UserRole search: String } type UserConnection { edges: [UserEdge!]! pageInfo: PageInfo! totalCount: Int! } type UserEdge { node: User! cursor: String! } type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! startCursor: String endCursor: String } type UserPayload { user: User errors: [Error!] } type DeletePayload { success: Boolean! errors: [Error!] } type Error { field: String message: String! }
Resolver Patterns
const resolvers = { Query: { user: async (_, { id }, context) => { return context.dataSources.users.findById(id) }, users: async (_, { page, limit, filter }, context) => { return context.dataSources.users.findAll({ page, limit, filter }) }, }, Mutation: { createUser: async (_, { input }, context) => { try { const user = await context.dataSources.users.create(input) return { user, errors: [] } } catch (error) { return { user: null, errors: [{ message: error.message }] } } }, }, User: { posts: async (user, { first }, context) => { return context.dataSources.posts.findByUserId(user.id, { first }) }, }, }
Error Handling Best Practices
Error Codes
enum ErrorCode { // Authentication INVALID_TOKEN = 'INVALID_TOKEN', TOKEN_EXPIRED = 'TOKEN_EXPIRED', UNAUTHORIZED = 'UNAUTHORIZED', // Authorization FORBIDDEN = 'FORBIDDEN', INSUFFICIENT_PERMISSIONS = 'INSUFFICIENT_PERMISSIONS', // Validation VALIDATION_ERROR = 'VALIDATION_ERROR', INVALID_INPUT = 'INVALID_INPUT', // Resource NOT_FOUND = 'NOT_FOUND', ALREADY_EXISTS = 'ALREADY_EXISTS', CONFLICT = 'CONFLICT', // Rate Limiting RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED', // Server INTERNAL_ERROR = 'INTERNAL_ERROR', SERVICE_UNAVAILABLE = 'SERVICE_UNAVAILABLE', }
HATEOAS (Hypermedia)
{ "success": true, "data": { "id": 123, "name": "John Doe", "email": "john@example.com" }, "links": { "self": "/api/v1/users/123", "posts": "/api/v1/users/123/posts", "update": "/api/v1/users/123", "delete": "/api/v1/users/123" } }
Caching Headers
# Cache for 1 hour Cache-Control: max-age=3600, public # No cache Cache-Control: no-store, no-cache, must-revalidate # ETag for conditional requests ETag: "abc123" If-None-Match: "abc123" # Last-Modified Last-Modified: Wed, 21 Oct 2024 07:28:00 GMT If-Modified-Since: Wed, 21 Oct 2024 07:28:00 GMT
Security Best Practices
- Always use HTTPS
- Validate all input - Never trust client data
- Rate limit endpoints - Prevent abuse
- Use proper authentication - JWT, OAuth 2.0
- Implement authorization - Check permissions
- Sanitize output - Prevent XSS
- Use parameterized queries - Prevent SQL injection
- Log security events - Audit trail
- Don't expose internals - Generic error messages
- Set security headers - CORS, CSP, etc.
API Documentation
Good Documentation Includes:
- Authentication guide
- Request/response examples
- Error code reference
- Rate limiting info
- Changelog/versioning
- SDKs and code samples
- Interactive playground (Swagger UI)
Common Pitfalls
- Inconsistent naming - Stick to one convention
- Missing validation - Always validate input
- No versioning - Version from the start
- Poor error messages - Be helpful, not cryptic
- No rate limiting - Protect from abuse
- Exposing internals - Don't leak stack traces
- Ignoring caching - Set proper cache headers
- No pagination - Always paginate lists
- Breaking changes - Use versioning properly
- Missing documentation - Document everything
Part of SuperAI GitHub - Centralized OpenCode Configuration