Add CI check for OpenAPI spec drift - #36
Merged
priscaenoch merged 1 commit intoJul 24, 2026
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
indexer/scripts/openapi-drift.js, which statically extracts every Express route registered inindexer/src/api.js,indexer/src/routes/admin.js, andindexer/src/billing/stripeWebhook.jsand reconciles it againstdocs/api/openapi.yaml'spaths:section.node indexer/scripts/openapi-drift.js --checkexits non-zero if any implemented route is undocumented or any documented route no longer exists, and is wired into a newopenapi-driftjob in.github/workflows/ci.yml. Also adds the initialdocs/api/openapi.yaml(documenting all 70 currently implemented routes) anddocs/OPENAPI.mdexplaining scope and regeneration.Context
Investigating this issue turned up something more basic than expected:
docs/api/openapi.yamldidn't exist in the repo at all.indexer/src/api.js'sGET /api/openapi.yamlroute doesfs.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.jsloads this same file at import time viafs.readFileSync, so that test file has been crashing before running a single assertion.Also:
swagger-jsdocis a dependency, but it's only used by the separate top-level API service (src/indexer/swaggerSpec.ts, a different Express app onPORT3000) — the indexer (indexer/,PORT3001, 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 byopenapi-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.mdfor 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-levelsrc/api/service, notindexer/). I documented thex-status: experimental/x-status: mockconvention indocs/OPENAPI.mdfor when one is added there.Before / After
Before: no spec file,
/api/openapi.yaml404s,/api/docsnever mounts,contract.test.jscrashes 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.jsdirectly. Instead I independently re-implemented its exact extraction/parsing logic in Python and ran it against this branch's actualindexer/src/*.jsanddocs/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 underpaths:matches the expected shape). Please runnode indexer/scripts/openapi-drift.js --checkfor real before merging as a final check on the actual script.Closes #23