mirror of
https://github.com/duthaho/claudekit.git
synced 2026-08-02 01:37:43 +03:00
feat: enhanced the writing skills
This commit is contained in:
@@ -0,0 +1,274 @@
|
||||
# API Governance & Tooling
|
||||
|
||||
Linters, docs generators, client generators, mock servers, and contract testing tools for an OpenAPI-based workflow. Focus on what is actually used in 2026.
|
||||
|
||||
---
|
||||
|
||||
## Why governance matters
|
||||
|
||||
Specs drift. A hand-written spec that is not linted in CI will accumulate missing `operationId`s, inline schemas, undocumented errors, and inconsistent naming within weeks. A generated spec (from FastAPI/NestJS) will drift toward whatever the framework emits, which is rarely idiomatic.
|
||||
|
||||
**Minimum bar for any new API:**
|
||||
1. Spec lives in version control
|
||||
2. A linter runs on every PR
|
||||
3. Docs regenerate on merge to main
|
||||
4. At least one generated client (or contract test) catches breaking changes
|
||||
|
||||
---
|
||||
|
||||
## Linters
|
||||
|
||||
All three below consume the same Spectral-compatible rule format.
|
||||
|
||||
| Tool | Language | Speed | When to pick |
|
||||
|------|----------|-------|--------------|
|
||||
| **Spectral** (Stoplight) | Node | Moderate | Default choice. Largest rule ecosystem, first-party Redocly + Zalando rulesets. |
|
||||
| **Redocly CLI** | Node | Moderate | Pick if you also want `bundle`, `split`, and `build-docs` in one tool. Ships its own opinionated ruleset. |
|
||||
| **Vacuum** (daveshanley) | Go | 10–20× faster | Pick for large specs (500+ operations) or monorepo CI where seconds matter. Drop-in Spectral rule compatibility. |
|
||||
|
||||
### Minimum Spectral ruleset
|
||||
|
||||
Save as `.spectral.yaml` in the spec directory:
|
||||
|
||||
```yaml
|
||||
extends:
|
||||
- spectral:oas # Base OpenAPI rules
|
||||
- spectral:asyncapi # If you mix in AsyncAPI
|
||||
|
||||
rules:
|
||||
# Every operation must be identified, tagged, and summarized
|
||||
operation-operationId: error
|
||||
operation-operationId-unique: error
|
||||
operation-tags: error
|
||||
operation-summary: error
|
||||
operation-description: warn
|
||||
|
||||
# Schemas must be referenced, not inlined
|
||||
no-inline-schemas:
|
||||
description: Request/response bodies must $ref a named schema.
|
||||
severity: error
|
||||
given: "$.paths.*.*.requestBody.content.*.schema"
|
||||
then:
|
||||
field: "$ref"
|
||||
function: truthy
|
||||
|
||||
# No 3.0-isms in a 3.1 document
|
||||
no-nullable:
|
||||
description: "Use type: [T, 'null'] instead of nullable: true in 3.1."
|
||||
severity: error
|
||||
given: "$..nullable"
|
||||
then:
|
||||
function: falsy
|
||||
|
||||
# Enforce camelCase on JSON properties
|
||||
camel-case-properties:
|
||||
description: Property names must be camelCase.
|
||||
severity: error
|
||||
given: "$.components.schemas..properties[*]~"
|
||||
then:
|
||||
function: pattern
|
||||
functionOptions:
|
||||
match: "^[a-z][a-zA-Z0-9]*$"
|
||||
|
||||
# Every operation declares at least one 4xx and one 5xx response
|
||||
operation-4xx-response:
|
||||
severity: error
|
||||
given: "$.paths.*.*.responses"
|
||||
then:
|
||||
function: schema
|
||||
functionOptions:
|
||||
schema:
|
||||
type: object
|
||||
patternProperties:
|
||||
"^4\\d\\d$": {}
|
||||
minProperties: 1
|
||||
|
||||
# Error bodies use application/problem+json
|
||||
error-uses-problem-json:
|
||||
description: 4xx/5xx responses must use application/problem+json.
|
||||
severity: warn
|
||||
given: "$.paths.*.*.responses[?(@property.match(/^[45]\\d\\d$/))].content"
|
||||
then:
|
||||
field: "application/problem+json"
|
||||
function: truthy
|
||||
```
|
||||
|
||||
### GitHub Actions CI snippet
|
||||
|
||||
```yaml
|
||||
# .github/workflows/api-spec.yml
|
||||
name: API spec
|
||||
on:
|
||||
pull_request:
|
||||
paths: ['spec/**']
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['spec/**']
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20' }
|
||||
|
||||
- name: Bundle spec
|
||||
run: npx @redocly/cli@latest bundle spec/openapi.yaml -o spec/openapi.bundled.yaml
|
||||
|
||||
- name: Lint with Spectral
|
||||
run: npx @stoplight/spectral-cli@latest lint spec/openapi.bundled.yaml --fail-severity=error
|
||||
|
||||
- name: Detect breaking changes vs main
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
git fetch origin main
|
||||
npx @redocly/cli@latest diff origin/main:spec/openapi.yaml spec/openapi.yaml --fail-on=breaking
|
||||
```
|
||||
|
||||
The `diff --fail-on=breaking` step blocks PRs that remove fields, change types, or rename operations — the most common accidental breakages.
|
||||
|
||||
---
|
||||
|
||||
## Docs Generators
|
||||
|
||||
| Tool | Style | When to pick |
|
||||
|------|-------|--------------|
|
||||
| **Scalar** | Modern three-column with built-in REST client | Default choice for new projects. Fast, polished, open source. |
|
||||
| **Redoc** | Classic three-column reference (Stripe-like) | Pick when you want the most battle-tested static docs. Works offline. |
|
||||
| **Redocly Portal** | Hosted docs with analytics, try-it, versioning | Pick when you need a revenue-class docs portal. Paid. |
|
||||
| **Swagger UI** | Interactive try-it | Pick only for internal/debug dashboards. Aesthetics lag behind Scalar/Redoc. |
|
||||
|
||||
### Scalar (recommended default)
|
||||
|
||||
```html
|
||||
<!-- docs.html -->
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><title>My API</title></head>
|
||||
<body>
|
||||
<script id="api-reference" data-url="/openapi.yaml"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Redoc (static build)
|
||||
|
||||
```bash
|
||||
npx @redocly/cli build-docs spec/openapi.yaml -o dist/api.html
|
||||
```
|
||||
|
||||
Deploy the single HTML file to any static host (Cloudflare Pages, S3, GitHub Pages).
|
||||
|
||||
---
|
||||
|
||||
## Client Generation
|
||||
|
||||
| Tool | Targets | When to pick |
|
||||
|------|---------|--------------|
|
||||
| **Kubb** | TypeScript (Zod, TanStack Query, SWR, MSW, Axios, Fetch) | Default for 2026 frontend. Plugin-based, generates exactly what you want, no framework bloat. |
|
||||
| **Orval** | TypeScript (React Query, SWR, Zod, MSW, Axios) | Close alternative to Kubb. Pick if you prefer a single-config approach. |
|
||||
| **openapi-generator** | 30+ languages (Python, Go, Java, Kotlin, Ruby, Rust, etc.) | Default for non-TypeScript languages. The workhorse, but generated code is heavier. |
|
||||
| **openapi-ts** (hey-api) | TypeScript only, lightweight | Pick when you want a minimal fetch wrapper with full types and zero framework coupling. |
|
||||
|
||||
### Kubb starter (TypeScript + TanStack Query + Zod)
|
||||
|
||||
```ts
|
||||
// kubb.config.ts
|
||||
import { defineConfig } from '@kubb/core'
|
||||
import { pluginOas } from '@kubb/plugin-oas'
|
||||
import { pluginTs } from '@kubb/plugin-ts'
|
||||
import { pluginZod } from '@kubb/plugin-zod'
|
||||
import { pluginClient } from '@kubb/plugin-client'
|
||||
import { pluginReactQuery } from '@kubb/plugin-react-query'
|
||||
|
||||
export default defineConfig({
|
||||
input: { path: './spec/openapi.yaml' },
|
||||
output: { path: './src/api/generated', clean: true },
|
||||
plugins: [
|
||||
pluginOas(),
|
||||
pluginTs(),
|
||||
pluginZod(),
|
||||
pluginClient({ importPath: '../client.ts' }),
|
||||
pluginReactQuery(),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### openapi-generator (Python / Go / Java / etc.)
|
||||
|
||||
```bash
|
||||
npx @openapitools/openapi-generator-cli generate \
|
||||
-i spec/openapi.yaml \
|
||||
-g python \
|
||||
-o clients/python \
|
||||
--additional-properties=packageName=acme_client,library=asyncio
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Mock Servers
|
||||
|
||||
| Tool | When to pick |
|
||||
|------|--------------|
|
||||
| **Prism** (Stoplight) | Run a mock server directly from your spec. Validates requests against the schema and returns examples. Best for frontend dev against an unfinished backend. |
|
||||
| **MSW** (Mock Service Worker) | Runs in the browser/Node for testing client code. Pair with Kubb's `@kubb/plugin-msw` to generate handlers from the spec. |
|
||||
|
||||
### Prism starter
|
||||
|
||||
```bash
|
||||
npx @stoplight/prism-cli mock spec/openapi.yaml --port 4010
|
||||
# Server at http://127.0.0.1:4010 responds based on the spec examples
|
||||
```
|
||||
|
||||
Add `--errors` to make Prism return the declared error responses when the request is invalid, useful for exercising error paths.
|
||||
|
||||
---
|
||||
|
||||
## Contract Testing
|
||||
|
||||
Tools that verify the running implementation still matches the spec.
|
||||
|
||||
| Tool | Approach | When to pick |
|
||||
|------|----------|--------------|
|
||||
| **Schemathesis** | Property-based fuzzing driven by the spec | Best signal per line of setup. Catches unhandled edge cases the developer never thought to test. |
|
||||
| **Dredd** | Replays documented examples against the server | Simple smoke-test. Good for regression on happy paths. |
|
||||
| **Pact** | Consumer-driven contracts (not spec-first) | Pick when consumers write the contracts rather than deriving from the server's OpenAPI. |
|
||||
|
||||
### Schemathesis in CI
|
||||
|
||||
```bash
|
||||
pipx install schemathesis
|
||||
schemathesis run spec/openapi.yaml \
|
||||
--base-url=http://localhost:3000/v1 \
|
||||
--checks=all \
|
||||
--hypothesis-max-examples=50
|
||||
```
|
||||
|
||||
Runs ~50 generated requests per operation and checks: status code validity, response schema conformance, `Content-Type` match, and absence of server errors (5xx).
|
||||
|
||||
---
|
||||
|
||||
## Governance checklist
|
||||
|
||||
Before calling an API "production-ready":
|
||||
|
||||
- [ ] Spec is in version control alongside the code
|
||||
- [ ] Spec is bundled (`redocly bundle`) and the bundled artifact is linted
|
||||
- [ ] Spectral (or equivalent) runs on every PR and blocks on errors
|
||||
- [ ] A breaking-change check runs on every PR (`redocly diff` or `oasdiff`)
|
||||
- [ ] Every operation has `operationId`, `tags`, `summary`, at least one `4xx` and at least one `5xx` response
|
||||
- [ ] Docs are regenerated on merge to main (Scalar, Redoc, or portal)
|
||||
- [ ] At least one generated client is compiled in CI — proves the spec is consumable
|
||||
- [ ] Contract tests run against a deployed preview before merge
|
||||
- [ ] A mock server (Prism) is available for consumer development
|
||||
|
||||
---
|
||||
|
||||
## Related
|
||||
|
||||
- [rest-naming.md](rest-naming.md) — URL and naming conventions
|
||||
- [http-status-codes.md](http-status-codes.md) — status code selection
|
||||
- [production-patterns.md](production-patterns.md) — idempotency, rate limiting, ETags, webhook signing
|
||||
- [openapi.tools](https://openapi.tools/) — community catalog of all OpenAPI tools
|
||||
Reference in New Issue
Block a user