Skip to content

feat: implement API versioning, deprecation strategy, and backwards-compatible schema evolution#69

Merged
DeFiVC merged 6 commits into
ChainLearnOfficial:mainfrom
PrimeFactor-Dev:feat/api-versioning
Jul 21, 2026
Merged

feat: implement API versioning, deprecation strategy, and backwards-compatible schema evolution#69
DeFiVC merged 6 commits into
ChainLearnOfficial:mainfrom
PrimeFactor-Dev:feat/api-versioning

Conversation

@PrimeFactor-Dev

Copy link
Copy Markdown
Contributor

Closes #12

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Documentation update

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, sets request.apiVersion = "v1", applies response envelope middleware
  • src/routes/v1/index.ts: Wraps existing route modules (auth, users, courses, quizzes, rewards, credentials) under V1 namespace

Response Envelope

  • src/middleware/response-envelope.ts: Adds meta.version, meta.timestamp, and meta.requestId to all successful V1 responses without double-wrapping

Backwards Compatibility

  • src/middleware/error-handler.ts: Modified 404 handler to redirect /api/* to /api/v1/* with 301 status, preserving existing clients

Deprecation Support

  • src/middleware/deprecation.ts: Middleware that adds Deprecation, Sunset, and Link headers for version deprecation

Type 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
  • Updated all existing e2e tests to use /api/v1/ paths

Current Behavior vs. New Behavior

Before: All endpoints under /api/* with no version prefix
After:

  • Versioned endpoints at /api/v1/*
  • /api/* redirects to /api/v1/* (301) for backwards compatibility
  • All V1 responses include meta object with version, timestamp, and requestId
  • Deprecation headers middleware ready for future version sunsets

Testing

  • TypeScript: npm run typecheck — passes with 0 errors
  • Lint: npm run lint — passes with 0 errors (warnings are pre-existing)
  • Contract tests: 18/18 passing — covers versioning, redirects, response envelope, health endpoints
  • E2E tests: 46/46 passing — all existing tests updated to use versioned paths
  • Unit tests: 75/77 passing (2 failures are pre-existing, unrelated to this change)

Breaking Changes

No. The backwards compatibility redirect ensures existing clients continue to work. All /api/* requests are transparently redirected to /api/v1/*.

Tradeoffs

  • Used URL-based versioning (/api/v1/) over header-based versioning for simplicity and debuggability
  • Response envelope adds meta field to existing responses rather than restructuring the entire response format
  • 301 redirect preserves backwards compatibility at the cost of one extra HTTP round-trip for legacy clients

Out of Scope

  • V2 route implementation (prepared for via the versioning infrastructure)
  • Contract testing CI pipeline (requires database setup)
  • Frontend migration to use /api/v1/ paths directly

Checklist

  • All existing tests pass (64/64 contract+e2e, 75/77 unit — 2 pre-existing failures)
  • TypeScript type checking passes
  • ESLint passes with 0 errors
  • No hardcoded secrets or tokens
  • Naming consistent with existing codebase
  • Multiple commits showing iterative development

- 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 DeFiVC left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. Type safety (, ): Using bypasses TypeScript. Extend or create a custom request type:

  2. 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

@DeFiVC
DeFiVC merged commit 75431a5 into ChainLearnOfficial:main Jul 21, 2026
2 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.

[Expert] Implement API versioning, deprecation strategy, and backwards-compatible schema evolution

2 participants