api-forge

$npx mdskill add EliasOulkadi/shokunin/api-forge

Design REST/GraphQL APIs with OpenAPI 3.1 specs and best practices.

  • Generates OpenAPI 3.1 specs from user requirements.
  • Depends on OpenAPI 3.1 specification and REST/GraphQL patterns.
  • Uses triggers like 'design an API' or 'rate limiting' to activate.
  • Outputs OpenAPI specs, endpoint designs, or audit reports.

SKILL.md

.github/skills/api-forgeView on GitHub ↗
---
name: api-forge
description: Design REST/GraphQL APIs with OpenAPI 3.1, error handling, pagination, rate limiting, webhooks, and idempotency. Use when user asks to design an API, create endpoints, define REST/GraphQL schema, or generate OpenAPI spec. Do NOT use for database schema design, frontend API integration, or non-HTTP protocols (gRPC, WebSocket, MQTT).
triggers:
  - "design an API"
  - "create endpoints"
  - "REST API"
  - "GraphQL schema"
  - "OpenAPI spec"
  - "rate limiting"
  - "webhooks"
  - "idempotency"
  - "API pagination"
  - "API versioning"
  - "API security"
negatives:
  - "database schema"
  - "frontend integration"
  - "gRPC"
  - "WebSocket"
  - "MQTT"
  - "database design"
license: MIT
compatibility: opencode
metadata:
  workflow: backend
  audience: developers
  version: "4.0.0"
  author: shokunin
---


# API Forge

Design APIs that developers love. Based on patterns from Stripe, GitHub, Twilio, Slack, and the OpenAPI 3.1 specification.

## Sub-Commands

| Command | Category | Description |
|---------|----------|-------------|
| `design` | Build | Design an API from user requirements. Generate OpenAPI 3.1 spec. |
| `endpoint` | Build | Design a single endpoint with all methods, parameters, responses, and error codes. |
| `audit` | Evaluate | Audit an existing API against design rules, security checklist, and anti-patterns. |
| `document` | Document | Generate API documentation from OpenAPI spec or route handlers. |
| `extract` | Document | Extract OpenAPI spec from existing route handlers. |
| `webhook` | Build | Design webhook delivery, retry, and signature verification. |

## Workflow

### Step 1: Determine API type

| Type | Use Case | Spec |
|------|----------|------|
| REST | CRUD, resource-oriented | OpenAPI 3.1 |
| GraphQL | Complex queries, multiple resources | Schema Definition Language |
| Webhook | Event-driven, async notifications | Standard webhooks (Stripe pattern) |

### Step 2: Define resources and naming

| Pattern | Example | Notes |
|---------|---------|-------|
| Nouns, plural | `/users`, `/orders` | Never verbs |
| Nested (max 2 levels) | `/users/{id}/orders` | Flat preferred over deep nesting |
| Actions as sub-resources | `/orders/{id}/cancel` | Only for non-CRUD operations |
| Query for filters | `/users?role=admin` | Not `/users/admins` |
| kebab-case for paths | `/order-items` | Not `/orderItems` |
| snake_case for fields | `first_name` | Not `firstName` in JSON:API |

### Step 3: Map HTTP methods

| Method | Purpose | Idempotent | Safe | Body |
|--------|---------|:--:|:--:|:--:|
| GET | Read resource | Yes | Yes | No |
| POST | Create resource | No | No | Yes |
| PUT | Full replace | Yes | No | Yes |
| PATCH | Partial update | No | No | Yes |
| DELETE | Remove resource | Yes | No | Optional |

### Step 4: Design response format

Stripe-style standard envelope:

```json
{
  "data": {},
  "meta": {
    "page": 1,
    "per_page": 25,
    "total": 100
  },
  "error": null,
  "request_id": "req_abc123"
}
```

If using JSON:API or GraphQL, use their standard envelopes instead.

### Step 5: Implement pagination

**Cursor-based** for production. Page-based only for admin/internal tools.

```json
GET /items?cursor=abc123&limit=25
{
  "data": [...],
  "meta": {
    "next_cursor": "def456",
    "has_more": true
  }
}
```

Cursor must be opaque (base64-encoded compound key). Never expose internal IDs. Maximum limit: 100. Default: 25.

## Error Handling

Every error response includes:
- `code`: machine-readable error code (`VALIDATION_ERROR`, `NOT_FOUND`, `RATE_LIMITED`)
- `message`: human-readable summary, max 150 chars
- `details`: array of field-level errors for validation
- `request_id`: UUIDv4 for debugging correlation
- `docs_url`: link to error documentation (optional, strongly recommended)

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Email is required",
    "details": [
      { "field": "email", "code": "required", "message": "Email is required" }
    ],
    "request_id": "req_a1b2c3d4e5f6",
    "docs_url": "https://docs.example.com/errors/validation"
  }
}
```

### Status codes — exact mapping

| Code | When | What to return |
|------|------|---------------|
| 200 | Success (GET, PUT, PATCH) | Resource + meta |
| 201 | Created (POST) | Created resource + `Location` header |
| 204 | No content (DELETE) | Empty body |
| 400 | Validation error | Error details + fields |
| 401 | Missing/invalid auth | Generic message. Never reveal which part of auth failed. |
| 403 | Insufficient permissions | Generic message |
| 404 | Resource not found | Minimal. Don't reveal if the resource ever existed. |
| 409 | Conflict (duplicate, stale version) | Details of conflicting field |
| 422 | Unprocessable entity | Validation details |
| 429 | Rate limited | `Retry-After` header (seconds) |
| 500 | Internal error | Generic message. No stack traces. No internal state. |
| 502 | Downstream failure | "Service temporarily unavailable" |
| 503 | Maintenance / overload | `Retry-After` header |

## Rate Limiting

### Algorithm decision

| Algorithm | Best for | Behavior |
|-----------|----------|----------|
| Token Bucket | General purpose, bursts allowed | Tokens refill at configurable rate. Allows bursts up to bucket size. |
| Sliding Window | Strict fairness, multi-tenant | Counts requests in rolling time window. No burst edge at boundaries. |
| Fixed Window | Simple, non-critical | Resets at interval. Budget edge problem at boundaries. |

**Default: Token Bucket.**

### Headers (every response)

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 42
X-RateLimit-Reset: 1700000000
```

Return `429 Too Many Requests` with `Retry-After` header when exceeded.

### Rate limit tiers (exact values)

| Tier | Requests | Window | Per |
|------|----------|--------|-----|
| Anonymous | 60 | 60s | IP |
| Authenticated | 1000 | 60s | User ID + endpoint group |
| Critical endpoints | 5 | 15min | IP + identifier |

Critical: login (5/15min per IP+username), password reset (3/60min per email), MFA (3/15min per user).

## Versioning

| Strategy | Example | When | Risk |
|----------|---------|------|------|
| URL path | `/v1/users` | Default for REST APIs | URL pollution |
| Header | `Accept: application/vnd.api+json;version=2` | Clean URLs needed | Harder to discover |
| Query param | `/users?version=2` | Simple, transitional | Cache poisoning risk |

Prefer URL path for public APIs. Deprecate with sunset headers. 6-month migration window minimum.

```
Deprecation: true
Sunset: Sat, 12 May 2027 00:00:00 GMT
```

## Webhooks

### Delivery format (Stripe pattern)

```json
{
  "id": "wh_abc123",
  "type": "order.created",
  "created": 1700000000,
  "data": {
    "id": "order_456",
    "status": "paid",
    "total": 2999
  }
}
```

### Delivery protocol

- Retry: exponential backoff (1s, 2s, 4s, 8s, 16s, 32s…)
- Max retries: 3. Max TTL: 24 hours.
- Expect 200 response within 5 seconds.
- Signature: HMAC-SHA256.

### Signature verification (exact implementation)

```
X-Webhook-Signature: t=1700000000,v1=abc123def456...
```

```typescript
function verifyWebhook(payload: string, signature: string, secret: string): boolean {
  const [timestampStr, signatures] = signature.split(',').map(s => s.trim())
  const timestamp = timestampStr.split('=')[1]

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatures.split('=')[1])
  )
}
```

## Idempotency

| Header | Value | TTL |
|--------|-------|-----|
| `Idempotency-Key` | UUIDv4 | 24 hours |

Return cached response (same status, same body) if same key seen within TTL. Return `409 Conflict` if different request body arrives with same key.

## API Security Checklist

- [ ] HTTPS enforced (HTTP → 301 redirect)
- [ ] TLS 1.2+ only (no TLS 1.0/1.1)
- [ ] CORS whitelist per environment, never `*` with credentials
- [ ] Input validation at boundary (Zod, Joi, Pydantic)
- [ ] Parameterized queries (no SQL injection). Never string interpolation.
- [ ] No secrets in responses, logs, or error messages
- [ ] Rate limiting on auth + password endpoints
- [ ] Request size limit: 1MB default, configurable per endpoint
- [ ] Body parsing limits (depth, field count, string length)
- [ ] Security headers: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Content-Security-Policy`

## OpenAPI 3.1 Generation

Every endpoint needs:
- `summary`: one sentence. Verb + resource.
- `parameters`: name, in, required, schema, description, example
- `responses`: every possible status code
- `requestBody` (for POST/PUT/PATCH): `content`, `schema`, `required`

## GraphQL

### Schema design

- Queries for read, Mutations for write, Subscriptions for real-time
- Max 3 nesting levels per query
- `@deprecated(reason: "Use fieldX instead")` for removals
- DataLoader for N+1 prevention
- Complexity limits: max depth 5, max cost 1000

### Error handling

```json
{
  "errors": [
    {
      "message": "Validation error",
      "extensions": {
        "code": "VALIDATION_ERROR",
        "field": "email",
        "request_id": "req_abc123"
      }
    }
  ]
}
```

## Anti-Patterns

| Anti-pattern | Fix |
|-------------|-----|
| Verbs in URL (`/getUsers`) | Use HTTP methods on noun resources |
| Page-based pagination for real-time data | Cursor-based with opaque cursors |
| No rate limit headers | Include `X-RateLimit-*` on every response |
| Returning 500 with stack trace | Log internally, return generic message |
| Breaking changes without migration | Version via URL, deprecation + sunset headers |
| No idempotency on POST creates | Add `Idempotency-Key` header support |
| Inconsistent error format across endpoints | Standard envelope for all errors |
| GraphQL without complexity limits | Implement query depth + cost analysis |
| Nested resources > 2 levels | Restructure. Deep nesting = tight coupling. |
| POST for everything | Use correct HTTP methods. GET=read, PUT=replace, PATCH=partial. |

## Production Checklist

- [ ] All endpoints documented with OpenAPI 3.1
- [ ] Envelope: `{ data, meta, error, request_id }` on every response
- [ ] Cursor-based pagination for public endpoints
- [ ] Rate limit headers on every response
- [ ] Rate limiting on auth endpoints (5/15min login, 3/60min reset)
- [ ] Idempotency-Key support on POST/PATCH
- [ ] Webhook signature verification (HMAC-SHA256)
- [ ] Security headers on every response
- [ ] CORS whitelist (never `*` with credentials)
- [ ] Input validation at boundary
- [ ] Parameterized queries everywhere
- [ ] Error responses include `request_id` and `code`
- [ ] Versioning strategy defined (URL path preferred)
- [ ] Deprecation + Sunset headers on deprecated endpoints

## Sources

- Stripe API Reference — idempotency, pagination, webhooks, error format
- GitHub REST API — resource naming, versioning
- Twilio API — webhook signature verification
- OpenAPI 3.1 Specification (openapis.org)
- JSON:API Specification (jsonapi.org)
- GraphQL Relay Connection Specification
- IETF RFC 7231 — HTTP semantics
- IETF RFC 6585 — Additional HTTP status codes
- Slack API — rate limiting headers

## Checklist

- [ ] Skill loads without errors in the AI agent
- [ ] YAML frontmatter is valid (description, compatibility, audience)
- [ ] Workflow section provides clear step-by-step instructions
- [ ] Error handling section covers common failure modes
- [ ] All referenced files (references/, scripts/, assets/) exist
- [ ] Skill triggers correctly for intended use cases
- [ ] No broken links or missing resources

More from EliasOulkadi/shokunin

SkillDescription
Aesthetic Web Design>
agent-toolsRun 150+ AI apps via inference.sh CLI - image generation, video creation, LLMs, search, 3D, Twitter automation. Models: FLUX, Veo, Gemini, Grok, Claude, Seedance, OmniHuman, Tavily, Exa, OpenRouter, and many more. Use when running AI apps, generating images/videos, calling LLMs, web search, or automating Twitter. Triggers: inference.sh, infsh, ai model, run ai, serverless ai, ai api, flux, veo, claude api, image generation, video generation, openrouter, tavily, exa search, twitter api, grok
auth-architectImplement authentication and authorization with OWASP Top 10 standards, OAuth 2.0 + OIDC, WebAuthn/Passkeys, session management, and RBAC/ABAC. Use when user asks to implement login, signup, authentication, authorization, JWT, OAuth, SSO, passkeys, MFA, or role-based access. Do NOT use for API key management (use api-forge), encryption at rest, or network-level security (firewalls, WAF).
business-proposalsGenerate sales outreach sequences, proposals, SOWs, investor pitch decks, and RFP responses. Covers cold email sequences, proposal structure, pricing tiers (Good-Better-Best), scope of work with exclusions, and 10-slide pitch deck.
ci-cdDesign CI/CD pipelines for GitHub Actions, GitLab CI, and CircleCI with matrix builds, test sharding, caching, Docker layer caching, OIDC auth, deployment strategies (rolling, blue-green, canary), auto-rollback, self-hosted runners, and environment protection with manual approvals. Use when user asks to set up CI/CD, write a pipeline, configure GitHub Actions/GitLab CI/CircleCI, automate deployments, or set up build/test/deploy workflows. Do NOT use for Dockerfile authoring (use docker), K8s manifests (use kubernetes), or Terraform config (use terraform).
communication>
component-forgeBuild production-grade components for React, Vue 3, and Svelte 5 with all states (loading, empty, error, success, idle), TypeScript strict, WCAG 2.2 accessibility, server components (RSC), and compound component patterns. Includes scaffold script, reference patterns, and template files for React and Vue. Use when user asks to create a UI component, frontend module, or design system component. Do NOT use for page layouts (use landing-craft), routing, or state management architecture (global stores).
comprehensive-reviewComprehensive code review using parallel specialized subagents. If a PR URL is provided, fetches PR details and can post comments. If no PR is provided, reviews the diff between the current branch and its base branch plus any uncommitted changes. CRITICAL: this skill is costly, don't use it unless user explicitly requested to use it.
cross-reviewDelegate code review to a subagent running a specific model. Use ONLY when user explicitly names a model to review changes ("review with opus", "use sonnet to review", "review with gemini"). The root agent reconstructs changes from conversation history and spawns a subagent with the code-review skill using the specified model. Do NOT use for general code review (use code-review skill instead), for reviewing PRs from git history, or when no model is specified by the user.
db-adminPostgreSQL database administration — backup/restore (pg_dump, PITR, WAL archiving), health monitoring (connections, bloat, cache hit ratio, dead tuples), connection pooling (PgBouncer), replication (streaming, logical), vacuum/autovacuum tuning, and scheduled backups with retention. Use when user asks to backup a database, restore from backup, monitor database health, set up replication, or perform DBA tasks. Do NOT use for schema design (use db-sculptor), query optimization (use db-sculptor), or migration planning (use db-sculptor).