smithery/frankxai

arcanea-api-design

Design APIs that developers love. RESTful principles, GraphQL patterns, versioning strategies, and the art of creating interfaces that are intuitive, consistent, and future-proof.

Installation

$ npx skills add smithery/frankxai --skill arcanea-api-design

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/frankxai · top by installs.

npx skills add smithery/frankxai

Browse all from smithery/frankxai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version2.0.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,934 B
  • docs SUMMARY.md 205 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

The API Design Codex

"An API is a user interface for developers. Design it with the same care you'd design a UI for users."


The API Design Philosophy

First Principles

GOOD APIs ARE:
• Predictable   - Behavior matches expectations
• Consistent    - Same patterns everywhere
• Simple        - Easy to use, hard to misuse
• Evolvable     - Can change without breaking
• Documented    - Self-describing where possible

GOOD APIs DO NOT:
• Surprise developers
• Require reading implementation
• Change behavior silently
• Expose internal details
• Force awkward workarounds

RESTful API Design

The REST Maturity Model

╔═══════════════════════════════════════════════════════════════════╗
║                    RICHARDSON MATURITY MODEL                       ║
╠═══════════════════════════════════════════════════════════════════╣
║                                                                    ║
║   LEVEL 0: The Swamp of POX                                       ║
║   Single endpoint, RPC-style                                       ║
║   POST /api → {action: "getUser", id: 1}                          ║
║                                                                    ║
║   LEVEL 1: Resources                                               ║
║   Multiple endpoints, still mostly POST                            ║
║   POST /users/1 → {action: "get"}                                 ║
║                                                                    ║
║   LEVEL 2: HTTP Verbs                                              ║
║   Proper use of GET, POST, PUT, DELETE                             ║
║   GET /users/1                                                     ║
║                                                                    ║
║   LEVEL 3: Hypermedia (HATEOAS)                                    ║
║   Responses include links to related actions                       ║
║   GET /users/1 → {..., links: [{rel: "orders", href: "/..."}]}    ║
║                                                                    ║
╚═══════════════════════════════════════════════════════════════════╝

Resource Naming

NOUNS, NOT VERBS:
✓ GET /users          ✗ GET /getUsers
✓ POST /orders        ✗ POST /createOrder
✓ DELETE /items/1     ✗ POST /deleteItem

PLURAL FOR COLLECTIONS:
✓ /users              ✗ /user
✓ /orders             ✗ /order

HIERARCHY FOR RELATIONSHIPS:
✓ /users/1/orders     ✗ /getUserOrders?userId=1
✓ /orders/1/items     ✗ /orderItems?orderId=1

KEBAB-CASE FOR MULTI-WORD:
✓ /user-profiles      ✗ /userProfiles
✓ /order-items        ✗ /order_items

HTTP Methods

┌────────┬────────────────┬──────────────┬──────────────┐
│ Method │ Purpose        │ Idempotent   │ Safe         │
├────────┼────────────────┼──────────────┼──────────────┤
│ GET    │ Read resource  │ Yes          │ Yes          │
│ POST   │ Create new     │ No           │ No           │
│ PUT    │ Replace all    │ Yes          │ No           │
│ PATCH  │ Partial update │ No*          │ No           │
│ DELETE │ Remove         │ Yes          │ No           │
└────────┴────────────────┴──────────────┴──────────────┘

*PATCH can be idempotent if designed carefully

Status Codes

2XX SUCCESS:
200 OK           - General success
201 Created      - Resource created (include Location header)
202 Accepted     - Processing started (async operations)
204 No Content   - Success with no body (DELETE, PUT)

4XX CLIENT ERRORS:
400 Bad Request  - Malformed request
401 Unauthorized - Authentication required
403 Forbidden    - Authenticated but not permitted
404 Not Found    - Resource doesn't exist
409 Conflict     - State conflict (e.g., duplicate)
422 Unprocessable - Valid syntax, invalid semantics

5XX SERVER ERRORS:
500 Internal     - Unexpected error
502 Bad Gateway  - Upstream service failed
503 Unavailable  - Temporarily overloaded
504 Gateway Timeout - Upstream timeout

Request/Response Design

// GOOD REQUEST
POST /api/v1/users
{
  "email": "[email protected]",
  "name": "John Doe",
  "role": "member"
}

// GOOD RESPONSE
{
  "data": {
    "id": "usr_123abc",
    "email": "[email protected]",
    "name": "John Doe",
    "role": "member",
    "createdAt": "2024-01-15T10:30:00Z"
  },
  "links": {
    "self": "/api/v1/users/usr_123abc",
    "orders": "/api/v1/users/usr_123abc/orders"
  }
}

// GOOD ERROR
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": [
      {
        "field": "email",
        "message": "Email format is invalid"
      }
    ]
  }
}

Pagination, Filtering, Sorting

Pagination Patterns

OFFSET-BASED (Simple, but has issues at scale):
GET /users?offset=20&limit=10

CURSOR-BASED (Better for large datasets):
GET /users?cursor=eyJpZCI6MTAwfQ&limit=10

Response:
{
  "data": [...],
  "pagination": {
    "total": 1000,
    "limit": 10,
    "nextCursor": "eyJpZCI6MTEwfQ",
    "prevCursor": "eyJpZCI6OTB9"
  }
}

Filtering

SIMPLE EQUALITY:
GET /users?status=active&role=admin

COMPARISON OPERATORS:
GET /orders?total[gte]=100&total[lte]=500
GET /users?createdAt[gt]=2024-01-01

ARRAY VALUES:
GET /users?status[]=active&status[]=pending

SEARCH:
GET /users?q=john
GET /products?search=widget

Sorting

SINGLE FIELD:
GET /users?sort=createdAt
GET /users?sort=-createdAt  (descending)

MULTIPLE FIELDS:
GET /users?sort=-createdAt,name
GET /users?orderBy=createdAt:desc,name:asc

API Versioning

Versioning Strategies

URL PATH (Most common, explicit):
GET /api/v1/users
GET /api/v2/users

QUERY PARAMETER:
GET /api/users?version=1
GET /api/users?v=2

HEADER (Clean URLs, hidden version):
GET /api/users
Accept: application/vnd.api+json;version=1

CONTENT NEGOTIATION:
GET /api/users
Accept: application/vnd.company.v2+json

Version Lifecycle

┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
│  Alpha  │────▶│  Beta   │────▶│ Stable  │────▶│ Sunset  │
└─────────┘     └─────────┘     └─────────┘     └─────────┘
    ↓               ↓               ↓               ↓
 Breaking        Breaking        Backward        Deprecation
 changes OK      with notice     compatible      warnings

Breaking vs Non-Breaking Changes

NON-BREAKING (Safe to add):
✓ New optional fields
✓ New endpoints
✓ New query parameters
✓ New response fields

BREAKING (Requires new version):
✗ Removing fields
✗ Renaming fields
✗ Changing field types
✗ Changing URL structure
✗ Changing validation rules

GraphQL Design

Schema Design

# Type definitions
type User {
  id: ID!
  email: String!
  name: String!
  orders(first: Int, after: String): OrderConnection!
  createdAt: DateTime!
}

type Order {
  id: ID!
  user: User!
  items: [OrderItem!]!
  total: Money!
  status: OrderStatus!
}

enum OrderStatus {
  PENDING
  CONFIRMED
  SHIPPED
  DELIVERED
  CANCELLED
}

# Connections for pagination
type OrderConnection {
  edges: [OrderEdge!]!
  pageInfo: PageInfo!
}

type OrderEdge {
  node: Order!
  cursor: String!
}

type PageInfo {
  hasNextPage: Boolean!
  endCursor: String
}

Query Design

type Query {
  # Single resource
  user(id: ID!): User

  # Collection with filtering
  users(
    filter: UserFilter
    orderBy: UserOrderBy
    first: Int
    after: String
  ): UserConnection!

  # Viewer pattern for current user
  viewer: User
}

input UserFilter {
  status: UserStatus
  role: UserRole
  search: String
}

input UserOrderBy {
  field: UserOrderField!
  direction: OrderDirection!
}

Mutation Design

type Mutation {
  # Create with input type
  createUser(input: CreateUserInput!): CreateUserPayload!

  # Update with partial input
  updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!

  # Delete returns deleted item or boolean
  deleteUser(id: ID!): DeleteUserPayload!
}

input CreateUserInput {
  email: String!
  name: String!
  role: UserRole
}

type CreateUserPayload {
  user: User
  errors: [UserError!]!
}

type UserError {
  field: String
  message: String!
  code: ErrorCode!
}

Security Best Practices

Authentication

TOKEN-BASED:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

API KEY:
X-API-Key: sk_live_abcd1234

OAUTH 2.0 FLOWS:
• Authorization Code - Web apps
• Client Credentials - Server-to-server
• PKCE - Mobile/SPA apps

Rate Limiting

HEADERS:
X-RateLimit-Limit: 1000        # Total allowed
X-RateLimit-Remaining: 999     # Remaining
X-RateLimit-Reset: 1609459200  # Reset timestamp

RESPONSE WHEN LIMITED:
HTTP/1.1 429 Too Many Requests
{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded",
    "retryAfter": 60
  }
}

Input Validation

ALWAYS VALIDATE:
□ Type (string, number, boolean)
□ Format (email, URL, UUID)
□ Length (min, max)
□ Range (min, max for numbers)
□ Allowed values (enums)
□ Required fields

SANITIZE:
□ Strip HTML
□ Escape special characters
□ Normalize unicode
□ Limit nested depth

Documentation

OpenAPI/Swagger

openapi: 3.0.0
info:
  title: User API
  version: 1.0.0

paths:
  /users:
    get:
      summary: List all users
      parameters:
        - name: status
          in: query
          schema:
            type: string
            enum: [active, inactive]
      responses:
        200:
          description: User list
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserList'

Self-Documenting APIs

// HATEOAS - Include discoverable links
{
  "data": { ... },
  "links": {
    "self": "/api/users/1",
    "edit": "/api/users/1",
    "delete": "/api/users/1",
    "orders": "/api/users/1/orders"
  },
  "actions": [
    {
      "name": "deactivate",
      "method": "POST",
      "href": "/api/users/1/deactivate"
    }
  ]
}

Error Handling

Error Response Structure

{
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "User with ID 'usr_123' not found",
    "target": "user",
    "details": [
      {
        "code": "INVALID_ID",
        "target": "id",
        "message": "ID format is invalid"
      }
    ],
    "innererror": {
      "trace": "abc123",
      "timestamp": "2024-01-15T10:30:00Z"
    }
  }
}

Error Code Conventions

Use consistent, meaningful codes:

AUTHENTICATION:
• AUTH_REQUIRED
• AUTH_INVALID_TOKEN
• AUTH_TOKEN_EXPIRED

AUTHORIZATION:
• FORBIDDEN
• INSUFFICIENT_PERMISSIONS

VALIDATION:
• VALIDATION_ERROR
• INVALID_FORMAT
• MISSING_FIELD
• FIELD_TOO_LONG

RESOURCE:
• RESOURCE_NOT_FOUND
• RESOURCE_ALREADY_EXISTS
• RESOURCE_CONFLICT

RATE LIMITING:
• RATE_LIMIT_EXCEEDED
• QUOTA_EXCEEDED

Quick Reference

API Design Checklist

□ Resources are nouns, plural
□ HTTP methods used correctly
□ Status codes are semantic
□ Consistent naming conventions
□ Pagination for lists
□ Filtering and sorting
□ Versioning strategy defined
□ Error format standardized
□ Rate limiting implemented
□ Authentication documented
□ OpenAPI spec available
□ Examples for all endpoints

REST vs GraphQL Decision

CHOOSE REST WHEN:
• Simple CRUD operations
• Caching is important
• Team knows REST well
• Multiple simple clients
• Request patterns are predictable

CHOOSE GRAPHQL WHEN:
• Complex, nested data
• Mobile apps with bandwidth concerns
• Frontend needs flexibility
• Multiple related resources
• Rapid frontend iteration

"The best API is invisible. Developers use it without thinking about it because it does what they expect."