agentleFS
andr-caandr-ca/agentharness/.claude/skills/api-design/SKILL.md

api-design

Skill1 starsChanged 2 months agoMITView on GitHub

Use when designing, reviewing, or evolving a REST or GraphQL API — resource naming, HTTP status codes, versioning strategy, error response shapes, pagination, and authentication patterns.

We split this skill into the rules it gives an agent

9 rules. 0 are common, 6 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 165 edited versions we read keep these.

  • 6

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

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

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use nouns, not verbs. Collections are plural.

    line 24
  2. Distinctive:

    Use RFC 9457 Problem Details:

    line 78
  3. Distinctive:

    Never include: stack traces, internal IDs, raw database errors.

    line 100
  4. Very specific:

    Prefer URL versioning for public APIs: /v1/users.

    line 106
  5. Distinctive:

    Never break a version in place.

    line 121
  6. Distinctive:

    Use cursor-based pagination for large or frequently-updated collections.

    line 128
  7. Very specific:

    Use input types for mutations — don't accept scalar arguments directly on mutations that take more than one field.

    line 165
  8. Very specific:

    Don't expose internal database IDs directly — use opaque, base64-encoded global IDs (User:123 → VXNlcjoxMjM=).

    line 169
  9. Distinctive:

    Before shipping a new API endpoint or changing an existing one:

    line 177

“Common” is measured: kept by at least 6 in 10 of the 165 edited versions of this file we could read. “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

API Design

Design choices for HTTP APIs (REST-style and GraphQL). The goal is an API that is consistent, predictable, and easy to evolve without breaking callers.

Reference: Google API Design Guide, JSON:API, RFC 9457 (Problem Details for HTTP APIs).


Resource naming (REST)

Use nouns, not verbs. Collections are plural.

GET    /users              → list users
POST   /users              → create a user
GET    /users/{id}         → get one user
PATCH  /users/{id}         → partial update
PUT    /users/{id}         → full replace
DELETE /users/{id}         → delete

GET    /users/{id}/posts   → posts belonging to a user
POST   /users/{id}/posts   → create a post for a user

Actions that don't map cleanly to CRUD go on a sub-resource with a verb:

POST   /users/{id}/activate
POST   /payments/{id}/refund

Naming rules:

  • kebab-case for multi-word path segments (/user-profiles, not /userProfiles or /user_profiles)
  • camelCase for JSON field names (consistent with most JS/TS clients)
  • IDs in path, filters/pagination in query string

HTTP status codes — use them correctly

CodeMeaningUse for
200OKSuccessful GET, PATCH, PUT
201CreatedSuccessful POST that creates a resource
204No ContentSuccessful DELETE or action with no response body
400Bad RequestClient sent invalid data (validation failure)
401UnauthorizedNot authenticated (no or invalid token)
403ForbiddenAuthenticated but not permitted for this resource
404Not FoundResource doesn't exist (or is hidden for auth reasons)
409ConflictDuplicate creation, optimistic lock failure
422Unprocessable EntitySyntactically valid but semantically invalid input
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server failure (never expose details)

Common mistakes:

  • 200 with {"success": false} in the body — use a 4xx status code
  • 404 for auth failures — use 401/403 (don't leak resource existence only when that itself is sensitive)
  • 500 for business logic failures — use a 4xx

Error response shape

Use RFC 9457 Problem Details:

{
  "type": "https://api.example.com/errors/validation-failed",
  "title": "Validation Failed",
  "status": 400,
  "detail": "The request body contains invalid fields.",
  "instance": "/requests/abc-123",
  "errors": [
    { "field": "email", "message": "must be a valid email address" },
    { "field": "age",   "message": "must be a positive integer" }
  ]
}
  • type — a stable URI identifying the error class (not a URL that must resolve, but it should if possible)
  • title — human-readable, stable label for the error class
  • detail — human-readable description of this specific error instance
  • errors — field-level validation failures (your extension, not in the spec)

Never include: stack traces, internal IDs, raw database errors.


Versioning

Prefer URL versioning for public APIs: /v1/users. It's explicit and easy to route at the infrastructure level.

/v1/users     → current stable version
/v2/users     → new version (breaking changes)

Rules:

  • Additive changes (new fields, new endpoints) are non-breaking — deploy without a version bump.
  • Breaking changes (removed fields, changed types, removed endpoints) require a new version.
  • Maintain at least one previous major version for a published deprecation window (typically 6–12 months).
  • Never break a version in place. Even a "fix" that changes a field type is a breaking change.

Pagination

Use cursor-based pagination for large or frequently-updated collections. Offset pagination is simpler but breaks under concurrent writes.

// Request
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20

// Response
{
  "data": [...],
  "pagination": {
    "next_cursor": "eyJpZCI6MTIwfQ",
    "has_more": true
  }
}
  • limit has a server-enforced maximum (e.g. 100); clients can request smaller pages but not larger.
  • next_cursor is opaque — clients must not parse it.
  • Return has_more: false and omit next_cursor on the last page.

Authentication

  • Bearer tokens in the Authorization header: Authorization: Bearer <token> — never in the URL.
  • API keys in a custom header (X-Api-Key) or Bearer — never in query params (they appear in server logs).
  • Return 401 for missing/invalid credentials; 403 for valid credentials that lack permission for the specific resource.

GraphQL-specific notes

  • Use input types for mutations — don't accept scalar arguments directly on mutations that take more than one field.
  • Implement cursor-based connection pagination for lists (Relay connection spec).
  • Don't expose internal database IDs directly — use opaque, base64-encoded global IDs (User:123 → VXNlcjoxMjM=).
  • Rate-limit by query complexity or depth, not just request count.

Review checklist

Before shipping a new API endpoint or changing an existing one:

  • Nouns used for resource names; collections plural; kebab-case paths
  • Correct HTTP method and status codes for each operation
  • Error responses follow RFC 9457 shape; no stack traces in responses
  • Additive changes do not bump the version; breaking changes do
  • Pagination implemented for any list endpoint that could return > 100 items
  • Authentication tokens sent in headers only (not URL or body)
  • Sensitive fields not exposed (passwords, internal IDs, PII beyond what's needed)
  • New endpoints documented (OpenAPI / README)

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.

Reviews can't be read right now.

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.