api-design-advanced

Advanced API design — per-language implementation patterns (TypeScript/Next.js, Go/net-http), anti-patterns (200 for everything, 500 for validation, contract breaking), and full pre-ship checklist.

8 stars

Best use case

api-design-advanced is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Advanced API design — per-language implementation patterns (TypeScript/Next.js, Go/net-http), anti-patterns (200 for everything, 500 for validation, contract breaking), and full pre-ship checklist.

Teams using api-design-advanced should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/api-design-advanced/SKILL.md --create-dirs "https://raw.githubusercontent.com/marvinrichter/clarc/main/skills/api-design-advanced/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/api-design-advanced/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How api-design-advanced Compares

Feature / Agentapi-design-advancedStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Advanced API design — per-language implementation patterns (TypeScript/Next.js, Go/net-http), anti-patterns (200 for everything, 500 for validation, contract breaking), and full pre-ship checklist.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# API Design — Advanced Patterns

This skill extends `api-design` with implementation patterns and anti-patterns. Load `api-design` first.

## When to Activate

- Implementing API handlers in TypeScript (Next.js) or Go
- Reviewing code for common API anti-patterns
- Applying the full pre-ship checklist to a new endpoint

---

## Implementation Patterns

### TypeScript (Next.js API Route)

```typescript
import { z } from "zod";
import { NextRequest, NextResponse } from "next/server";

const createUserSchema = z.object({
  email: z.string().email(),
  name: z.string().min(1).max(100),
});

const PROBLEM_CONTENT_TYPE = "application/problem+json";

export async function POST(req: NextRequest) {
  const body = await req.json();
  const parsed = createUserSchema.safeParse(body);

  if (!parsed.success) {
    return NextResponse.json({
      type: "https://api.example.com/problems/validation-failed",
      title: "Validation Failed",
      status: 422,
      detail: "One or more fields failed validation.",
      errors: parsed.error.issues.map(i => ({ field: i.path.join("."), detail: i.message })),
    }, { status: 422, headers: { "Content-Type": PROBLEM_CONTENT_TYPE } });
  }

  const user = await createUser(parsed.data);

  return NextResponse.json(
    { data: user },
    { status: 201, headers: { Location: `/api/v1/users/${user.id}` } },
  );
}
```

### Go (net/http)

```go
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    var req CreateUserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        writeProblem(w, r, http.StatusBadRequest,
            "https://api.example.com/problems/bad-request", "Bad Request",
            "Invalid request body.")
        return
    }

    if err := req.Validate(); err != nil {
        writeProblem(w, r, http.StatusUnprocessableEntity,
            "https://api.example.com/problems/validation-failed", "Validation Failed",
            err.Error())
        return
    }

    user, err := h.service.Create(r.Context(), req)
    if err != nil {
        switch {
        case errors.Is(err, domain.ErrEmailTaken):
            writeProblem(w, r, http.StatusConflict,
                "https://api.example.com/problems/email-taken", "Email Taken",
                "This email is already registered.")
        default:
            writeProblem(w, r, http.StatusInternalServerError,
                "about:blank", "Internal Server Error", "")
        }
        return
    }

    w.Header().Set("Location", fmt.Sprintf("/api/v1/users/%s", user.ID))
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(map[string]any{"data": user})
}

// writeProblem writes an RFC 7807 Problem Details response.
func writeProblem(w http.ResponseWriter, r *http.Request, status int,
                  type_, title, detail string) {
    w.Header().Set("Content-Type", "application/problem+json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(map[string]any{
        "type": type_, "title": title, "status": status,
        "detail": detail, "instance": r.RequestURI,
    })
}
```

---

## Anti-Patterns

### Using 200 OK for Every Response

**Wrong:**
```json
HTTP/1.1 200 OK
Content-Type: application/json

{ "success": false, "error": "User not found" }
```

**Correct:**
```json
HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "User abc-123 not found.",
  "instance": "/api/v1/users/abc-123"
}
```

**Why:** Returning 200 for errors forces clients to parse the body to detect failure, breaks HTTP caches and load balancers, and defeats the purpose of the status code system.

---

### Returning 500 for Validation Errors

**Wrong:**
```json
HTTP/1.1 500 Internal Server Error
Content-Type: application/json

{ "error": "Invalid email format" }
```

**Correct:**
```json
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/validation-failed",
  "title": "Validation Failed",
  "status": 422,
  "errors": [
    { "field": "email", "detail": "must be a valid email address" }
  ]
}
```

**Why:** Validation errors are client mistakes (4xx), not server failures (5xx) — misusing 500 triggers false alerts, hides real server errors, and confuses API consumers.

---

### Breaking the API Contract Without a Version Bump

**Wrong:**
```yaml
# v1 response — field renamed in-place, silently breaking all clients
responses:
  '200':
    content:
      application/json:
        schema:
          properties:
            full_name: { type: string }  # was "name" — breaking change without /v2/
```

**Correct:**
```yaml
# /api/v1/ keeps "name" intact; /api/v2/ introduces "full_name"
# Deprecate v1 with Sunset header over a 6-month notice period
Sunset: Sat, 01 Jan 2027 00:00:00 GMT
```

**Why:** Renaming, removing, or retyping fields in an existing version silently breaks all existing clients; any breaking change must be introduced under a new API version.

---

## API Design Checklist

Before shipping a new endpoint:

- [ ] Resource URL follows naming conventions (plural, kebab-case, no verbs)
- [ ] Correct HTTP method used (GET for reads, POST for creates, etc.)
- [ ] Appropriate status codes returned (not 200 for everything)
- [ ] Input validated with schema (Zod, Pydantic, Bean Validation)
- [ ] Error responses use RFC 7807 Problem Details (`Content-Type: application/problem+json`)
- [ ] Pagination implemented for list endpoints (cursor or offset)
- [ ] Authentication required (or explicitly marked as public)
- [ ] Authorization checked (user can only access their own resources)
- [ ] Rate limiting configured
- [ ] Response does not leak internal details (stack traces, SQL errors)
- [ ] Consistent naming with existing endpoints (camelCase vs snake_case)
- [ ] OpenAPI spec written **before** implementation (`api/v1/openapi.yaml`)
- [ ] Spec linted (`spectral lint`) with zero errors
- [ ] Types/stubs generated from spec — not hand-written
- [ ] Breaking change detection configured in CI (`oasdiff`)
- [ ] Every operation has non-empty `description` (not just `summary`)
- [ ] Every parameter has `description` + realistic `example`
- [ ] Every schema property has `description` + `example`
- [ ] `x-codeSamples` added for curl + TypeScript + Python + Go
- [ ] `x-stability` set (stable / beta / experimental)
- [ ] CHANGELOG.md updated for new/changed/deprecated endpoints

## Reference

- `api-design` — core REST patterns (resource naming, status codes, response format, auth, rate limiting, versioning)
- `api-contract` — Contract-First workflow (OpenAPI spec, code generation, CI breaking-change detection)

Related Skills

typography-design

8
from marvinrichter/clarc

Typography as a creative discipline: typeface selection criteria, type pairing (serif + sans, display + body), modular scale systems, line-height and tracking ratios, hierarchy construction, and web/mobile rendering considerations. The decisions behind design tokens, not the tokens themselves.

typescript-patterns-advanced

8
from marvinrichter/clarc

Advanced TypeScript — mapped types, template literal types, conditional types, infer, type guards, decorators, async patterns, testing with Vitest/Jest, and performance. Extends typescript-patterns.

tdd-workflow-advanced

8
from marvinrichter/clarc

TDD anti-patterns — writing code before tests, testing implementation details instead of behavior, using waitForTimeout as a sync strategy, chaining tests that share state, mocking the system under test instead of its dependencies.

swift-patterns-advanced

8
from marvinrichter/clarc

Advanced Swift patterns — property wrappers, result builders, Combine basics, opaque & existential types, macro system, advanced generics, and performance optimization. Extends swift-patterns.

serverless-patterns-advanced

8
from marvinrichter/clarc

Advanced Serverless patterns — Lambda idempotency (Lambda Powertools + DynamoDB persistence layer), Lambda cost model (pricing formula, break-even vs containers), and CloudWatch Insights observability queries for cold starts, duration, and errors.

security-review-advanced

8
from marvinrichter/clarc

Security anti-patterns — localStorage token storage (XSS risk), trusting client-side authorization checks, reflecting full error details to clients, blacklist vs whitelist input validation, using npm install instead of npm ci in CI pipelines.

sdk-design-patterns

8
from marvinrichter/clarc

SDK design patterns — API ergonomics, backward compatibility (semantic versioning, deprecation), multi-language SDK generation (openapi-generator vs Speakeasy), error hierarchy design, SDK testing strategies, and documentation as first-class SDK artifact.

rust-testing-advanced

8
from marvinrichter/clarc

Advanced Rust testing anti-patterns and corrections — cfg(test) placement, expect() over unwrap(), mockall expectation ordering, executor mixing (#[tokio::test] vs block_on), PgPool isolation with

rust-patterns-advanced

8
from marvinrichter/clarc

Advanced Rust patterns — zero-cost abstractions, proc macros, unsafe FFI, WASM, Axum web architecture, trait objects vs generics, and performance profiling.

python-testing-advanced

8
from marvinrichter/clarc

Advanced Python testing — async testing with pytest-asyncio, exception/side-effect testing, test organization, common patterns (API, database, class methods), pytest configuration, and CLI reference. Extends python-testing.

python-patterns-advanced

8
from marvinrichter/clarc

Advanced Python patterns — concurrency (threading, multiprocessing, async/await), hexagonal architecture with FastAPI, RFC 7807 error handling, memory optimization, pyproject.toml tooling, and anti-patterns. Extends python-patterns.

presentation-design

8
from marvinrichter/clarc

Presentation structure, narrative design, and slide layout principles. Covers the problem-solution-evidence arc, slide density rules (one idea per slide), slide type catalogue, opening hooks, and closing patterns. Use when structuring any slide deck — conference talk, demo, investor pitch, or team update.