agentleFS
VelimirMuellerVelimirMueller/ai-development-skills/skills/backend/design-http-api/SKILL.md

design-http-api

Skill7 starsChanged 15 days agoMITView on GitHub

Use when adding or changing HTTP endpoints, or when the frontend needs typed calls — sets the OpenAPI 3.1 contract, RFC 9457 errors, cursor pagination, idempotency keys, versioning, and a generated typed client behind the frontend fetcher seam.

  • Installs packages

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    No committed spec and no generator: full setup (steps 3 to 7).

    line 27
  2. Very specific:

    Check each line with npm view <pkg> version / go list -m -versions.

    line 54

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Design HTTP API

Rules and rationale: api-contract-patterns.md. Per-track generation, client wiring, code: api-codegen-patterns.md. Layer paths: service-layout.md. Versions: stack-versions.md.

The contract is a committed OpenAPI 3.1 file. Code and client both derive from it, so they cannot drift.

1. Audit (change nothing)

cat .claude/stack-profile.md ~/.claude/stack-profile.md 2>/dev/null   # backend.track, frontend.framework, package_manager
ls openapi.json openapi.yaml oapi-codegen.yaml src/api/schema.d.ts src/libs/api-client.ts 2>/dev/null
grep -rn "application/problem+json" src internal 2>/dev/null | head -3        # error format in place?
grep -rnE "cursor|next_cursor|nextCursor|offset=|page=" src internal 2>/dev/null | head   # pagination style
grep -rniE "idempotency-key|If-Match|ETag" src internal 2>/dev/null | head
grep -rnE "app\.(get|post|put|patch|delete)\(|\.openapi\(|@router\.|HandleFunc" src internal 2>/dev/null | wc -l

Record: where the spec comes from (code or file), which errors are problem+json, which list endpoints use offset, which unsafe endpoints lack idempotency. Never ask what the profile or repo answers.

2. Decide

  • No committed spec and no generator: full setup (steps 3 to 7).
  • Spec exists: apply only the missing rules (step 5 list) and the missing client wiring.
  • backend.track is nextjs: use build-nextjs-backend for route handlers; this skill still applies to the contract rules. supabase: PostgREST is the API; use set-up-supabase. Stop here for those unless the repo also ships its own HTTP service.
  • Spec committed, git diff --exit-code openapi.json clean after regeneration, client builds: report "already in place".

3. Detect track

backend.trackSource of truthWhy
honoZod route schemas via @hono/zod-openapi, emitted as openapi.jsonOne schema validates, types and documents. No second file to keep in sync
fastapiPydantic models and route signatures, emitted by app.openapi()Same reason; FastAPI already emits 3.1
goopenapi.yaml written first, oapi-codegen strict serverGo struct tags cannot carry enums, bounds or nullability; the spec is reviewed in the PR and the compiler forces every handler to match

Frontend client: frontend.framework any; the client is plain TypeScript (openapi-typescript + openapi-fetch).

4. Install only what is missing

# TS server: already present from scaffold-hono-service (@hono/zod-openapi, zod)
# Go server
go get -tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest
go get github.com/oapi-codegen/nethttp-middleware@latest
# Frontend (or the repo that owns the client)
pnpm add openapi-fetch
pnpm add -D openapi-typescript

Check each line with npm view <pkg> version / go list -m -versions. openapi-typescript 7.13 declares a typescript ^5 peer; with TypeScript 6 or 7 npm refuses to install, pnpm warns. The output is a .d.ts file and works; allow the peer in pnpm.peerDependencyRules and say so in the PR.

5. Apply the contract rules

Each rule is in api-contract-patterns.md; check the API against this list and fix the delta.

  1. Errors: every 4xx/5xx is application/problem+json (RFC 9457), declared in the spec with the Problem schema. Validation failures are 400 with an errors[] extension. Use the scaffold's seam (src/platform/problem.ts, internal/transport/httpapi/problem.go, platform/problem.py).
  2. Lists: cursor pagination, limit capped, { items, nextCursor }. No offset.
  3. Unsafe retries: POST that creates or charges accepts Idempotency-Key; same key and body replays the stored response.
  4. Names: plural kebab-case paths, camelCase JSON, operationId on every operation (it becomes the client method name), ISO 8601 UTC timestamps, string IDs.
  5. Versioning: additive changes only, no /v2 until a breaking change cannot be avoided. CI runs oasdiff breaking against the base branch.
  6. Concurrency: ETag + If-Match on resources that users edit concurrently; If-None-Match on large cacheable reads.

6. Generate and wire

  1. Emit the spec and commit it (commands per track in api-codegen-patterns.md).
  2. Client types: pnpm exec openapi-typescript openapi.json -o src/api/schema.d.ts. Commit the output.
  3. Client seam: src/libs/api-client.ts builds createClient<paths> on the frontend fetcher seam, so cookies, CSRF and 401 refresh are not redone. The seam needs one small export; see the section "Wire into the fetcher seam" in api-codegen-patterns.md and fetcher.md.
  4. Scripts: api:spec (emit), api:types (generate), api:check (regenerate and git diff --exit-code).
  5. CI: run api:check and oasdiff breaking origin/main:openapi.json openapi.json.

Go: //go:generate go tool oapi-codegen -config oapi-codegen.yaml ../../openapi.yaml next to the generated package; go generate ./... and git diff --exit-code in CI.

7. Verify

In a Vite frontend typecheck is tsc -b; tsc --noEmit checks zero files there (see ../../frontend/_shared/conventions.md).

pnpm api:check                         # spec and types regenerate with no diff
pnpm typecheck                 # a call with a wrong param type fails to compile
curl -si 'localhost:3000/<list-path>?limit=0' | sed -n '1p;/content-type/Ip'
# expect: HTTP/1.1 400 ... content-type: application/problem+json
oasdiff breaking origin/main:openapi.json openapi.json    # expect: no output (exit 0)

Go: go generate ./... && git diff --exit-code && go vet ./.... Idempotency: send the same POST with one key twice; the second response is identical and no second row exists.

References

  • api-contract-patterns.md: problem details, pagination, idempotency, versioning, conditional requests, naming.
  • api-codegen-patterns.md: spec emission per track, typed client, fetcher wiring.
  • ../../core/_shared/security-baseline.md: input validation and error disclosure.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.