feat: implement API versioning, deprecation strategy, and backwards-compatible schema evolution#69
Merged
DeFiVC merged 6 commits intoJul 21, 2026
Conversation
- Create src/routes/versioning.ts to register versioned routes under /api/v1/ - Create src/routes/v1/index.ts to wrap existing route modules - Add response envelope middleware that injects meta.version, meta.timestamp, and meta.requestId - Update server.ts to use registerVersionedRoutes() instead of direct route imports
- Create src/middleware/deprecation.ts for Sunset/Deprecation headers - Create src/types/api/v1.ts with ApiResponse, PaginatedResponse, and V1 type definitions
- Test versioned routes under /api/v1/ include response envelope with meta.version - Test backwards compatibility redirects from /api/* to /api/v1/* - Test health endpoints remain unaffected by versioning - Test 404 for unknown versioned routes
- Modify 404 handler to redirect unversioned /api/* paths to /api/v1/* - Returns 301 redirect for backwards compatibility with existing clients
- Update all e2e test URLs from /api/* to /api/v1/* - Tests now hit versioned routes directly instead of relying on redirects
The credential service validates quiz questions before checking for existing credentials. Test mocks were missing the questions field, causing 'Quiz has no questions' errors instead of the expected conflict/flow behavior.
DeFiVC
approved these changes
Jul 21, 2026
Contributor
There was a problem hiding this comment.
Review: Request Changes
What's Good
- Clean implementation: → → modules separation
- Backwards-compatible 301 redirect from to is well done
- E2E tests properly updated to versioned paths
- Excellent PR description with tradeoffs documented
Issues to Fix
-
Type safety (, ): Using bypasses TypeScript. Extend or create a custom request type:
-
Unused type definitions (): These V1 types are defined but never imported/used in route handlers or middleware. Either use them in the response envelope or remove until needed to avoid dead code.
Minor (Non-blocking)
- The field addition in is a good mock fix but could be a separate commit
- Contract tests using without DB setup are more like route-registration tests than true contract tests — worth noting for future work
Checklist
- CI green
- No scope creep
- Detailed description
- Multiple commits
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.
Closes #12
Type of Change
Summary
Implements URL-based API versioning with
/api/v1/prefix, backwards-compatible redirects from/api/*to/api/v1/*, response envelope with version metadata, deprecation headers middleware, V1 type contracts, and comprehensive contract tests.Motivation / Context
Fixes #12 — The API had no versioning strategy. All endpoints were under
/api/with no version prefix, making it impossible to maintain backwards compatibility when evolving the API. Breaking changes would immediately break the frontend, mobile apps, and third-party integrations.Detailed Changes
Versioning Infrastructure
src/routes/versioning.ts: Registers all versioned routes under/api/v1/prefix, setsrequest.apiVersion = "v1", applies response envelope middlewaresrc/routes/v1/index.ts: Wraps existing route modules (auth, users, courses, quizzes, rewards, credentials) under V1 namespaceResponse Envelope
src/middleware/response-envelope.ts: Addsmeta.version,meta.timestamp, andmeta.requestIdto all successful V1 responses without double-wrappingBackwards Compatibility
src/middleware/error-handler.ts: Modified 404 handler to redirect/api/*to/api/v1/*with 301 status, preserving existing clientsDeprecation Support
src/middleware/deprecation.ts: Middleware that addsDeprecation,Sunset, andLinkheaders for version deprecationType Contracts
src/types/api/v1.ts: TypeScript interfaces for V1 API responses (ApiResponse, PaginatedResponse, CourseSummaryV1, etc.)Tests
tests/contract/api-contract.test.ts: 18 tests covering versioned routes, backwards compatibility redirects, response envelope, health endpoints, and 404 handling/api/v1/pathsCurrent Behavior vs. New Behavior
Before: All endpoints under
/api/*with no version prefixAfter:
/api/v1/*/api/*redirects to/api/v1/*(301) for backwards compatibilitymetaobject with version, timestamp, and requestIdTesting
npm run typecheck— passes with 0 errorsnpm run lint— passes with 0 errors (warnings are pre-existing)Breaking Changes
No. The backwards compatibility redirect ensures existing clients continue to work. All
/api/*requests are transparently redirected to/api/v1/*.Tradeoffs
/api/v1/) over header-based versioning for simplicity and debuggabilitymetafield to existing responses rather than restructuring the entire response formatOut of Scope
/api/v1/paths directlyChecklist