frontier-python-ts/skills/rest-api-design/SKILL.md
Use when designing, reviewing, or documenting HTTP REST API endpoints, resources, contracts, or schemas.
npx skillsauth add jon23d/skillz rest-api-designInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
/users, /orders. Never /createUser or /getOrders./users, not /user./pending-orders, not /pendingOrders or /PendingOrders.| Method | Use Case | Idempotent? | Safe? |
|--------|----------|-------------|-------|
| GET | Read resources | Yes | Yes |
| POST | Create new resource | No | No |
| PUT | Replace complete resource | Yes | No |
| PATCH | Partial update | No | No |
| DELETE | Remove resource | Yes | No |
POST /projects/{id}/tasksGET /tasks/{taskId}/orgs/{id}/repos/{id}/issues is the limit.For operations that don't fit CRUD (e.g., "publish", "ban", "archive"):
PATCH /articles/{id} with { "status": "published" }PUT /articles/{id}/publicationPOST /articles/{id}/actions/publish (last resort)GET /user-profilesPOST /projects (not POST /createProject)GET /users (except singletons like /me)q: Search query stringlimit: Max results per pageoffset: Starting index (0-based)page: Page number (1-based)fields: Comma-separated list of fields to includeembed: Comma-separated list of related resources to includesortBy: Sort field namesnake_case field names in JSON (created_at, user_id). That is the FastAPI + Pydantic default and it matches the SQLAlchemy model field names, which keeps the serialisation layer thin.openapi-typescript — there is no camelCase translation layer. See the openapi-codegen skill.alias_generator=to_camel to "translate" at the API edge. It creates two names for the same field (DB vs wire) and the cost compounds.GET /v1/usersAccept: application/vnd.myapi.v1+json{
"data": [ ...items... ],
"meta": {
"pagination": {
"total": 100,
"count": 20,
"perPage": 20,
"currentPage": 2,
"totalPages": 5,
"links": {
"next": "https://api.example.com/v1/users?page=3",
"prev": "https://api.example.com/v1/users?page=1"
}
}
}
}
Always wrap collections in a data key. Never return naked arrays.
ISO 8601 UTC: "createdAt": "2023-10-27T14:30:00.000Z". Always UTC (Z suffix). Never epoch seconds.
String-based IDs (UUIDs or prefixed IDs like usr_123abc) to prevent enumeration. Avoid sequential integers for public interfaces.
{
"type": "about:blank",
"title": "Invalid Request",
"status": 400,
"detail": "Email is required.",
"instance": "/v1/users",
"errors": [
{ "field": "email", "message": "Must be a valid email address" }
]
}
Success (2xx): 200 OK, 201 Created (with Location header), 202 Accepted (async), 204 No Content (deletion/update with no body).
Client Error (4xx): 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed, 409 Conflict, 422 Unprocessable Entity, 429 Too Many Requests.
Server Error (5xx): 500 Internal Server Error, 503 Service Unavailable.
The universal rules above describe what to ship. FastAPI is how to ship it in this harness. The fastapi, pydantic, and openapi-codegen skills cover the details; the load-bearing rules that affect API design are:
response_model= is mandatory on every route. It populates /openapi.json, strips fields that aren't in the schema, and is the contract the frontend regenerates its types from. A route without response_model ships unknown to the frontend.status_code= is explicit. Default is 200 — set 201 for create, 204 for delete-with-no-body, 202 for async-accepted.operation_id= is stable. The generated frontend client method names are derived from this; a refactor that renames the handler function silently changes the client's method names unless operation_id is pinned.tags=[...] on the router — groups the OpenAPI spec by resource.responses={401: {...}, 404: {...}, ...} — document every non-success status code the frontend needs to handle. Undocumented codes are a contract bug.HTTPException(status_code=..., detail="...") for the happy-path cases. For RFC 7807 conformance with field-level errors, use a shared ErrorResponse Pydantic model and register an exception handler that returns it.tenant_id, user_id, or role from the request body — only from dependencies (get_current_user, get_tenant_db). See the multi-tenancy skill.200 OK for errors → use 4xx/5xxPOST for updates → use PATCH/PUTresponse_model= → the OpenAPI spec has no success-response schema, and the generated frontend client gets unknown everywhereoperation_id= → frontend client method names depend on FastAPI's defaults and silently change when you rename a handleralias_generator=to_camel on the response schema → creates two names for the same field; pick snake_case and stick with itdevelopment
Use when adding or modifying environment variable handling in TypeScript projects or monorepos — especially when using process.env directly, missing startup validation, sharing env schemas across packages, or encountering "undefined is not a string" errors at runtime from missing env vars.
testing
Use when creating a new skill, editing an existing skill, writing a SKILL.md, or verifying a skill works before deployment.
development
React UI design principles and conventions. Load when building or modifying any user interface or React components. Covers application type detection, visual standards, component design and structure, Mantine (business apps) and Tailwind (consumer apps), accessibility, responsiveness, state management, data fetching, testing, and in-app help patterns.
development
Use when setting up ESLint and/or Prettier in a TypeScript project, adding linting to an existing TypeScript codebase, or configuring typescript-eslint, eslint-config-prettier, or related packages.