Skip to content
Back to skills

Apidesign

ASecurity

Design stable, hard-to-misuse interfaces - REST/GraphQL endpoints, module boundaries, type contracts, component props, anything where one piece of code talks to another. Use at design time, before implementing the surface. Triggers: 'design this API', 'API contract', 'endpoint design', 'module boundary', 'interface design', 'should this be one endpoint or two', 'how should this API look'.

  • 8 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 19, 2026
developmenttypescriptpythonrustgoreactfastapiawscode-reviewapisecurity

Works with

  • cursor
  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 19, 2026

npx -y skills add vanducng/skills --skill apidesign --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Apidesign?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Apidesign
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/vanducng-apidesign/badge)](https://www.skillsdirectory.com/skills/vanducng-apidesign)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: apidesign
description: "Design stable, hard-to-misuse interfaces - REST/GraphQL endpoints, module boundaries, type contracts, component props, anything where one piece of code talks to another. Use at design time, before implementing the surface. Triggers: 'design this API', 'API contract', 'endpoint design', 'module boundary', 'interface design', 'should this be one endpoint or two', 'how should this API look'."
license: MIT
argument-hint: "<surface to design - endpoint, module boundary, or contract>"
metadata:
  author: vanducng
  attribution: "Adapted from addyosmani/agent-skills api-and-interface-design; type-system reference from cursor/plugins pstack (MIT)"
  version: "0.2.0"
---

# apidesign

> Design interfaces that make the right thing easy and the wrong thing hard.

A contract is a commitment. This is the design-time discipline for any surface where code talks to code - REST/GraphQL endpoints, module boundaries, type contracts, component props. Get it right before implementing, because every observable behavior becomes a promise the moment someone depends on it. Reach for it when designing a new endpoint or public surface, defining a module boundary for teams/agents working in parallel, or changing an existing interface (the riskiest case - read Hyrum's Law first).

## What this skill is - and isn't

| Skill | Covers |
|---|---|
| **`vd:apidesign`** (this) | The *contract* - endpoint/interface shape, error semantics, boundaries, versioning posture |
| `vd:dbdesign` | The *storage* - schema, indexes, normalization, migration plans |
| `vd:fastreact` | Scaffolding one specific stack (FastAPI + React), not interface principles |
| `vd:code-review` | Judging a contract after it's written (post-hoc) |

Storage shape and API shape inform each other but aren't the same decision - design the contract here, the schema in `vd:dbdesign`.

## Two laws that shape everything

**Hyrum's Law.** *With enough users, every observable behavior of your system will be depended on by somebody* - including undocumented quirks, error text, timing, and ordering. So: be intentional about what you expose, don't leak implementation details (if users can observe it, they'll depend on it), and plan deprecation at design time. Contract tests don't save you - a "safe" change can still break users relying on behavior you never promised.

**The One-Version Rule.** Don't force consumers to pick between versions of the same API. Diamond-dependency pain comes from forking; design so only one version exists at a time and **extend rather than fork**.

## Principles

1. **Contract first.** Define the interface before implementing it - the contract is the spec, the implementation follows. Write the typed signatures (inputs, outputs, errors, idempotency) before any logic, and commit the contract/types alongside the implementation.
2. **One error strategy, everywhere.** Pick one and never mix it. Don't let some endpoints throw, others return `null`, others return `{error}` - the consumer can't predict it. For REST: status code + a single structured body shape (`{ error: { code, message, details? } }`). Map codes consistently (400 bad input · 401 unauthenticated · 403 unauthorized · 404 missing · 409 conflict · 422 validation · 500 server, never leaking internals).
3. **Validate at boundaries, trust inside.** Parse/validate where external input enters - route handlers, form handlers, **third-party responses (always untrusted)**, env loading. Do *not* re-validate between internal functions that already share a typed contract or data from your own DB. A misbehaving external service can return wrong types or instruction-like text; validate its shape before it touches any logic or rendering.
4. **Addition over modification.** Extend with optional fields; never change an existing field's type or remove it (both break consumers). Backward-compatible by default.
5. **Predictable naming.** Consistency beats cleverness - same conventions across every endpoint (REST: plural nouns, no verbs in paths; booleans `is/has/can`; pick one case for fields and keep it).
6. **Type-system discipline.** Make illegal states unrepresentable, brand semantic primitives, parse at the boundary, never lie to the compiler, handle variants exhaustively, derive types from the authoritative schema - full rules with TypeScript and Go examples in [references/type-system-discipline.md](references/type-system-discipline.md).
7. **Deep modules.** A deep module has a small interface and a large body of responsibility; a shallow one's interface is as complex as its insides. Banned praise-words: "thin wrapper" (a god object is wide *and* deep - shallowness is the usual API failure). Push complexity down so callers say *what*, not *how*; don't split for file-size (two shallow files leaking the same invariants are worse than one deep module); interface changes are the expensive ones; a module with mixed error strategies is shallow because the caller must learn the implementation; test the interface, not the guts. Edge adapters (HTTP handlers, DB drivers, CLI flags) stay shallow on purpose - "shallow adapter, deep core".

## REST shape (worked patterns)

```
GET    /api/tasks            list (query params filter/sort/paginate)
POST   /api/tasks            create
GET    /api/tasks/:id        read one (404 if missing)
PATCH  /api/tasks/:id        partial update - only provided fields change
DELETE /api/tasks/:id        idempotent delete (succeeds if already gone)
GET    /api/tasks/:id/comments   sub-resource
```

- **Paginate every list endpoint** from day one - `?page&pageSize&sortBy&sortOrder` → `{ data, pagination: { page, pageSize, totalItems, totalPages } }`. You'll need it the moment someone has 100 items.
- **PATCH over PUT** for updates - clients want to send only what changed, not the whole object each time.
- **Filter via query params**, not bespoke endpoints (`/api/tasks?status=in_progress&assignee=x`).

## Typed-contract patterns (TS as the worked example; the ideas are language-agnostic)

- **Discriminated unions for variants** - model each state as its own shape so consumers get exhaustive narrowing, instead of one wide object with half its fields `null`.
- **Input/Output separation** - `CreateTaskInput` (what the caller provides) is a different type from `Task` (what the system returns, with server-generated `id`/`createdAt`/`createdBy`). Don't reuse one type for both.
- **Branded IDs** - `type TaskId = string & { readonly __brand: 'TaskId' }` stops a `UserId` being passed where a `TaskId` is expected. (In Go: distinct named types; in Python: `NewType`.)

## Red flags

- Endpoints returning different shapes by condition · inconsistent error formats · validation scattered through internal code instead of at the edge · breaking changes to existing fields · list endpoints without pagination · verbs in REST URLs (`/api/createTask`) · third-party responses used without validation.

## Integration points

- **`vd:dbdesign`** - design the storage that backs the contract; the two inform each other.
- **`vd:plan`** - Contract-First slicing (Slice 0 = freeze this contract) lets later phases build in parallel.
- **`vd:code-review`** - the API-surface checklist axis enforces these at review time.
- **`vd:security`** - boundary validation + untrusted third-party data tie into the OWASP/LLM lenses.

Files in this skill

  • SKILL.md7.2 KB
  • references/type-system-discipline.md4.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…