src/skills/api-specs-openapi/SKILL.md
OpenAPI 3.1 specification, schema design, code generation
npx skillsauth add agents-inc/skills api-specs-openapiInstall 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.
Quick Guide: Use OpenAPI 3.1 for API contracts. 3.1 is a superset of JSON Schema Draft 2020-12 -- use
type: ["string", "null"]instead ofnullable: true. Define all reusable schemas incomponents/schemasand reference with$ref. Always includeoperationIdon every operation (it becomes the client method name). Useopenapi-typescriptto generate zero-runtime TypeScript types andopenapi-fetchfor a 6kb type-safe fetch client.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use OpenAPI 3.1 syntax -- type: ["string", "null"] NOT the 3.0 nullable: true keyword)
(You MUST define reusable schemas in components/schemas and reference with $ref -- NO inline schema duplication)
(You MUST include operationId on every path operation -- it becomes the generated client method name)
(You MUST use openapi-typescript for type generation and import types with import type -- types are zero-runtime)
</critical_requirements>
Auto-detection: OpenAPI, openapi, swagger, openapi-typescript, openapi-fetch, createClient, paths, components, schemas, operationId, $ref, discriminator, oneOf, allOf, anyOf, openapi: "3.1", spec-first, API contract, API specification, code generation, schema design
When to use:
$ref compositionWhen NOT to use:
Key patterns covered:
$ref composition, oneOf/allOf/anyOf, discriminatorsopenapi-typescript v7openapi-fetchDetailed Resources:
$ref compositionopenapi-fetch clientThe spec IS the contract. An OpenAPI document is the single source of truth for your API's shape. Types, documentation, client SDKs, and server validation are all derived from it -- never maintained separately.
OpenAPI 3.1 aligns with JSON Schema Draft 2020-12. This means any valid JSON Schema is a valid OpenAPI schema. Use type arrays for nullable (["string", "null"]), if/then/else for conditional schemas, and standard JSON Schema vocabulary.
Spec-first (design-first) is recommended for stable, multi-consumer APIs. Define the contract first, get feedback from consumers via mocks, then implement. Code-first works for rapid prototypes where the spec is generated from annotations.
Use spec-first when:
Use code-first when:
Every OpenAPI 3.1 document has four required top-level fields: openapi, info, paths (or webhooks), and implicitly components for reusable schemas.
openapi: "3.1.0"
info:
title: Jobs API
version: "1.0.0"
description: Job listings and applications
servers:
- url: https://api.example.com/v1
paths:
/jobs:
get:
operationId: listJobs
# ...
components:
schemas:
Job:
# ...
Why good: operationId becomes the client method name, servers enables environment switching, schemas in components are reusable via $ref
See examples/core.md for complete spec with paths, parameters, and responses.
OpenAPI 3.1 uses JSON Schema Draft 2020-12. Key differences from 3.0: nullable is removed, use type arrays instead. exclusiveMinimum/exclusiveMaximum are numbers, not booleans.
# 3.1 nullable syntax
type: ["string", "null"]
# NOT 3.0 syntax:
# type: string
# nullable: true
components:
schemas:
Salary:
type: object
required: [min, max, currency]
properties:
min:
type: integer
minimum: 0
max:
type: integer
minimum: 0
currency:
type: string
minLength: 3
maxLength: 3
description: ISO 4217 currency code
Why good: aligns with standard JSON Schema, tooling ecosystem understands it natively, required array is explicit
See examples/core.md for enum, format, and composition examples.
Define schemas once in components/schemas, reference everywhere with $ref. Use allOf to extend base schemas, oneOf for polymorphism with discriminators.
components:
schemas:
PaginatedResponse:
type: object
required: [data, pagination]
properties:
pagination:
$ref: "#/components/schemas/Pagination"
JobListResponse:
allOf:
- $ref: "#/components/schemas/PaginatedResponse"
- type: object
properties:
data:
type: array
items:
$ref: "#/components/schemas/Job"
Why good: single source of truth, changes propagate automatically, generated types reflect composition
See examples/core.md for oneOf with discriminator and allOf extension patterns.
Operations define HTTP methods on paths. Always include operationId, tags, parameter schemas, and all response codes.
paths:
/jobs/{jobId}:
get:
operationId: getJob
tags: [Jobs]
parameters:
- name: jobId
in: path
required: true
schema:
type: string
format: uuid
responses:
"200":
description: Job details
content:
application/json:
schema:
$ref: "#/components/schemas/Job"
"404":
description: Job not found
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
Why good: operationId drives codegen method names, explicit 404 response documents error cases, format: uuid aids validation
See examples/core.md for query parameters, request bodies, and pagination.
Use openapi-typescript v7 to generate zero-runtime types from your spec. Types are generated as .d.ts files and imported with import type.
npx openapi-typescript ./api/openapi.yaml -o ./api/schema.d.ts
import type { paths, components } from "./api/schema.d.ts";
// Access schema types
type Job = components["schemas"]["Job"];
type Error = components["schemas"]["Error"];
// Access response types
type JobListResponse =
paths["/jobs"]["get"]["responses"]["200"]["content"]["application/json"];
Why good: zero runtime cost, types stay in sync with spec, no manual interface maintenance
See examples/codegen.md for CLI options, redocly.yaml multi-schema config, and programmatic API.
Use openapi-fetch (6kb) for a type-safe client that infers request/response types from generated types. No codegen needed beyond the types.
import createClient from "openapi-fetch";
import type { paths } from "./api/schema.d.ts";
const client = createClient<paths>({ baseUrl: "https://api.example.com/v1" });
// Fully typed -- path params, query, body, response
const { data, error } = await client.GET("/jobs/{jobId}", {
params: { path: { jobId: "abc-123" } },
});
if (error) {
// error is typed to the spec's error response schema
console.error(error);
return;
}
// data is typed to the spec's 200 response schema
console.log(data.title);
Why good: 6kb with virtually zero runtime, no manual generics, path/query/body/response all type-checked against the spec
See examples/codegen.md for middleware, auth headers, and error handling.
</patterns><decision_framework>
Is this a public or multi-consumer API?
|-- YES --> Spec-first (design contract, get feedback, then implement)
+-- NO --> Is this a rapid prototype?
|-- YES --> Code-first (generate spec from annotations)
+-- NO --> Does your framework generate OpenAPI from code?
|-- YES --> Code-first (framework handles spec generation)
+-- NO --> Spec-first (write the YAML, generate types)
Need to share fields across schemas?
|-- YES --> allOf with a base $ref schema
+-- NO --> Need polymorphism (multiple possible shapes)?
|-- YES --> oneOf with discriminator
+-- NO --> Need to combine constraints?
|-- YES --> allOf (all must match)
+-- NO --> Simple schema with $ref for nested objects
| Need | Tool | When |
| -------------------------- | ----------------------- | ------------------------------------------- |
| TypeScript types from spec | openapi-typescript v7 | Always -- zero-runtime type generation |
| Type-safe fetch client | openapi-fetch | Frontend or service-to-service calls |
| Full SDK generation | @hey-api/openapi-ts | Need Zod schemas, query hooks, or full SDKs |
| Spec validation/linting | Redocly CLI | CI pipeline, pre-commit checks |
</decision_framework>
<red_flags>
High Priority:
nullable: true -- removed in OpenAPI 3.1, use type: ["string", "null"]$ref to components/schemas -- creates driftoperationId on operations -- generated clients get ugly auto-generated namesopenapi-typescript to generate themopenapi: "3.0.x" when 3.1 is available -- misses JSON Schema alignmentMedium Priority:
Error schema -- inconsistent error shapes across endpointsrequired array on object schemas -- all properties become optional by defaulttype: object without additionalProperties: false when extra fields should be rejected4xx/5xx responses -- consumers don't know what error shapes to expectGotchas & Edge Cases:
$ref siblings are ignored in 3.0 but allowed in 3.1 -- description next to $ref now works in 3.1exclusiveMinimum/exclusiveMaximum changed from boolean (3.0) to number (3.1) -- exclusiveMinimum: 0 means "greater than 0"discriminator does not affect validation -- it's a hint for code generators, not a constraintdiscriminator.propertyName must be a required string property at the same schema leveldiscriminator oneOf are not considered -- only $ref entries workopenapi-typescript generates .d.ts files -- these are type-only, no runtime codeopenapi-fetch data is only present for 2xx responses, error for 4xx/5xx -- always check which is definedopenapi-typescript v7 uses redocly.yaml for multi-schema config -- globbing is deprecated</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST use OpenAPI 3.1 syntax -- type: ["string", "null"] NOT the 3.0 nullable: true keyword)
(You MUST define reusable schemas in components/schemas and reference with $ref -- NO inline schema duplication)
(You MUST include operationId on every path operation -- it becomes the generated client method name)
(You MUST use openapi-typescript for type generation and import types with import type -- types are zero-runtime)
Failure to follow these rules will cause schema drift, broken codegen, and type mismatches between spec and implementation.
</critical_reminders>
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
tools
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events