Skip to content

Add CI check for OpenAPI spec drift - #36

Merged
priscaenoch merged 1 commit into
octraban:mainfrom
samuel2926i39-art:ci/openapi-spec-drift-check-issue-23
Jul 24, 2026
Merged

Add CI check for OpenAPI spec drift#36
priscaenoch merged 1 commit into
octraban:mainfrom
samuel2926i39-art:ci/openapi-spec-drift-check-issue-23

Conversation

@samuel2926i39-art

Copy link
Copy Markdown

Summary

Adds indexer/scripts/openapi-drift.js, which statically extracts every Express route registered in indexer/src/api.js, indexer/src/routes/admin.js, and indexer/src/billing/stripeWebhook.js and reconciles it against docs/api/openapi.yaml's paths: section. node indexer/scripts/openapi-drift.js --check exits non-zero if any implemented route is undocumented or any documented route no longer exists, and is wired into a new openapi-drift job in .github/workflows/ci.yml. Also adds the initial docs/api/openapi.yaml (documenting all 70 currently implemented routes) and docs/OPENAPI.md explaining scope and regeneration.

Context

Investigating this issue turned up something more basic than expected: docs/api/openapi.yaml didn't exist in the repo at all. indexer/src/api.js's GET /api/openapi.yaml route does fs.existsSync(openApiPath) and 404s otherwise — so that endpoint has always been returning 404, and /api/docs (swagger-ui) was never actually mounted. Separately, indexer/test/api/contract.test.js loads this same file at import time via fs.readFileSync, so that test file has been crashing before running a single assertion.

Also: swagger-jsdoc is a dependency, but it's only used by the separate top-level API service (src/indexer/swaggerSpec.ts, a different Express app on PORT 3000) — the indexer (indexer/, PORT 3001, the service this issue's Files list points at) never used it. So the spec here isn't "generated from annotations vs. hand-maintained" — it's neither yet, and I've made it the former: mechanically generated by openapi-drift.js, which is what makes an exact drift check possible without hand-authoring 70 route descriptions.

Scope note: generated entries assert route existence (path + HTTP method) with a generic response, not full request/response schemas. Hand-authoring accurate schemas for ~70 routes was out of scope for a drift check — see docs/OPENAPI.md for why, and how to enrich individual routes later without breaking the check (the generator only ever adds missing entries; it never rewrites or deletes existing ones, so hand-added detail is safe).

Mock/experimental endpoints: none currently exist in indexer/ as far as I could find (docs/STATUS.md's mock/stub tracking, referenced by this issue, covers the separate top-level src/api/ service, not indexer/). I documented the x-status: experimental / x-status: mock convention in docs/OPENAPI.md for when one is added there.

Before / After

Before: no spec file, /api/openapi.yaml 404s, /api/docs never mounts, contract.test.js crashes at import, nothing checks routes against docs.
After: spec exists and matches implementation exactly (verified locally by re-deriving the route list independently — see Testing), CI fails if either side changes without the other.

Testing

I don't have Node installed in this environment (and was asked not to install dependencies), so I could not run indexer/scripts/openapi-drift.js directly. Instead I independently re-implemented its exact extraction/parsing logic in Python and ran it against this branch's actual indexer/src/*.js and docs/api/openapi.yaml: 70 implemented routes, 70 documented, 0 missing, 0 stale. I also validated the YAML's indentation/structure line-by-line (no tabs, balanced quotes, every line under paths: matches the expected shape). Please run node indexer/scripts/openapi-drift.js --check for real before merging as a final check on the actual script.

Closes #23

docs/api/openapi.yaml didn't exist, so GET /api/openapi.yaml always
404'd and indexer/test/api/contract.test.js (which loads it) crashed
at import time. Adds indexer/scripts/openapi-drift.js, which
statically extracts every Express route registered in
indexer/src/api.js, indexer/src/routes/admin.js, and
indexer/src/billing/stripeWebhook.js and reconciles it against the
spec's `paths:` section; `--check` exits non-zero on any mismatch and
is wired into a new openapi-drift CI job. The generator is additive
only (never rewrites or deletes existing entries), so hand-added
detail on any path survives future regenerations.

The initial docs/api/openapi.yaml documents all 70 currently
implemented routes; entries are route-existence stubs (summary + a
generic response), not full request/response contracts — see
docs/OPENAPI.md for scope, regeneration, and how to mark future
experimental/mock-data endpoints.
@priscaenoch
priscaenoch merged commit 1463667 into octraban:main Jul 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep the published OpenAPI specification in sync with the implemented routes

2 participants