The OpenAPI specification is the API contract and must be treated as such. This document outlines how spec changes are managed, reviewed, and validated.
The canonical OpenAPI specification is stored in version control:
- JSON Format:
docs/openapi.json(used by CI validation) - YAML Format:
docs/openapi.yaml(human-readable reference)
Both formats are generated from route JSDoc annotations and stored in src/config/openapi.js.
Modify route definitions and update JSDoc annotations to reflect the API change.
npm run openapi:generateThis produces deterministic output (sorted keys) so the diff is meaningful and stable.
git diff docs/openapi.json
git diff docs/openapi.yamlThe diff should clearly show:
- Additive changes (new fields, new endpoints) - reviewed for completeness
- Breaking changes (removed fields, type narrowing) - flagged for discussion
- Renames (field/endpoint name changes) - verified not to break clients
git add docs/openapi.json docs/openapi.yaml
git commit -m "docs: update OpenAPI spec for [feature]"Runs in CI (npm run openapi:check) to verify:
-
Byte-Stability: The committed spec matches the generated spec exactly
- Ensures no manual edits or accidental diffs
- Deterministic sorting prevents flakes
-
Response Example Validation: Response examples match their schemas
- Catches outdated examples in documentation
-
Auth Scheme Verification: Required security schemes are defined
- Ensures all endpoints are properly protected
-
Shared Schema Verification: Standard response types exist
Error,ValidationError,UnauthorizedError,NotFoundError
In .github/workflows/ci.yml, the openapi:check step:
- Regenerates the spec from annotations
- Compares against committed snapshot
- Fails if they differ (spec changes require updated snapshot)
- Reports all path counts and defined schemas
Future CI enhancements can flag breaking changes more loudly:
- Removed endpoints (HTTP 404)
- Removed fields (data loss)
- Type narrowing (validation failures)
- Renamed fields (client updates required)
The generate-openapi.js script ensures byte-stable output:
const stableSpec = sortObjectKeys(spec);This means:
- Same input always produces same bytes
- Diffs are meaningful (not noise from reordering)
- CI checks for staleness are non-flaky
OpenAPI spec is built from:
- Route JSDoc annotations (see
src/routes/**/*.js) - Shared schemas and security definitions (see
src/config/openapi.js) - Response examples (inline in route handlers)
- ✓ Canonical OpenAPI snapshot is committed in
docs/ - ✓ CI fails when generated spec differs from committed snapshot
- ✓ Intentional changes are reviewed via snapshot diff in PR
- ✓ Byte-stable output ensures diffs are meaningful
- ✓ All documented endpoints are reachable and respond correctly