Skip to content

feat(backend): Add API Versioning with Header-Based Version Negotiation - #312

Merged
devJaja merged 2 commits into
Epta-Node:mainfrom
jotel-dev:feat/api-versioning
Aug 25, 2026
Merged

feat(backend): Add API Versioning with Header-Based Version Negotiation#312
devJaja merged 2 commits into
Epta-Node:mainfrom
jotel-dev:feat/api-versioning

Conversation

@jotel-dev

Copy link
Copy Markdown
Contributor

Closes #268

Summary

This PR implements header-based API versioning to enable backward-compatible API evolution without breaking existing clients. The implementation allows clients to specify their desired API version via the API-Version header, while maintaining support for legacy versions and providing enhanced response formats for newer versions.

The versioning system includes:

  • Header-based version negotiation using the API-Version header
  • Automatic fallback to the latest version when no header is provided
  • Deprecation warnings for older API versions via standard HTTP headers
  • Version-specific response formats with enhanced metadata in v2
  • Comprehensive unit tests covering all version negotiation scenarios

Changes

New Files Created

backend/src/api/middleware/versioning.ts

  • Implements versioningMiddleware for header-based version negotiation
  • Validates requested API versions against supported versions
  • Adds version-specific response headers (X-API-Version, Deprecation, Sunset)
  • Provides utility functions for version parsing and comparison
  • Gracefully handles missing configuration with sensible defaults

backend/src/api/middleware/versioning.test.ts

  • Comprehensive unit tests for version negotiation middleware
  • Tests for default version fallback behavior
  • Tests for client-specified version handling
  • Tests for unsupported/invalid version rejection
  • Tests for deprecation header functionality
  • Tests for version parsing and comparison utilities

backend/src/api/routes/v1/tasks.ts

  • Implements v1 task routes with original response format
  • Maintains backward compatibility with existing clients
  • Simple response structure without additional metadata
  • Supports all task operations: create, list, get, delete

backend/src/api/routes/v2/tasks.ts

  • Implements v2 task routes with enhanced response format
  • Includes comprehensive metadata in responses:
    • _meta object with version, timestamp, requestId, apiVersion
    • _links object with HATEOAS-style navigation links
    • Enhanced pagination metadata with hasNextPage/hasPreviousPage
  • Improved error responses with version information
  • Future-proof structure for additional v2 features

Modified Files

backend/src/api/app.ts

  • Added versioningMiddleware to the middleware chain
  • Implemented version-specific routing logic for task endpoints
  • Creates separate router instances for v1 and v2 task routes
  • Routes requests to appropriate version based on negotiated API version
  • Maintains backward compatibility for existing endpoints

backend/src/config/index.ts

  • Added API_LATEST_VERSION configuration (default: "2.0")
  • Added API_SUPPORTED_VERSIONS configuration (default: "1.0,1.1,2.0")
  • Added API_V1_SUNSET_DATE configuration for deprecation scheduling
  • Environment-based configuration for flexible deployment

backend/src/api/types/express.d.ts

  • Extended Express.Locals interface with apiVersion property
  • Enables type-safe access to negotiated version in route handlers
  • Maintains TypeScript type safety throughout the application

backend/src/api/routes/tasks.ts

  • Marked original createTasksRouter as deprecated
  • Maintained for backward compatibility during transition period
  • Documentation updated to recommend version-specific routers

Technical Implementation Details

Version Negotiation Flow

  1. Request Processing:

    • Client sends API-Version header (optional)
    • Middleware validates version against supported versions
    • Invalid/unsupported versions return 400 error with supported version list
  2. Version Resolution:

    • If header omitted → defaults to API_LATEST_VERSION
    • If header provided → validates against API_SUPPORTED_VERSIONS
    • Negotiated version stored in res.locals.apiVersion for downstream use
  3. Response Headers:

    • X-API-Version: Echoes the version used for the request
    • Deprecation: Added for deprecated versions (major version < latest)
    • Sunset: Added when sunset date is configured for deprecated versions

Version-Specific Routing

The routing logic uses simple version prefix matching:

  • Versions starting with "1." → v1 router (original format)
  • Versions 2.0+ → v2 router (enhanced format)
  • Default fallback → v2 router

Response Format Differences

v1 Response Format:
{
"taskId": "task_abc123",
"dagPreview": {...},
"status": "queued"
}

v2 Response Format:
{
"data": {
"taskId": "task_abc123",
"dagPreview": {...},
"status": "queued"
},
"_meta": {
"version": "2.0",
"timestamp": "2026-08-25T06:00:00.000Z",
"requestId": "uuid-here",
"apiVersion": "2.0"
},
"_links": {
"self": "/api/tasks/task_abc123",
"stream": "/api/tasks/task_abc123/stream"
}
}

Testing

Unit Tests

✅ Version negotiation with valid client-specified versions
✅ Default version fallback when header is omitted
✅ Rejection of unsupported versions with proper error messages
✅ Rejection of invalid version formats
✅ Deprecation header functionality for v1.x versions
✅ Sunset header functionality when configured
✅ Version parsing and comparison utilities
✅ Configuration fallback when config not loaded

Integration Testing

✅ TypeScript compilation successful
✅ Build process completes without errors
✅ Middleware chain integration with existing routes
✅ Version-specific routing logic verification

Manual Testing Scenarios

  1. Request without API-Version header → defaults to v2.0
  2. Request with API-Version: 1.0 → uses v1 format with deprecation headers
  3. Request with API-Version: 2.0 → uses v2 enhanced format
  4. Request with API-Version: 3.0 → returns 400 error with supported versions
  5. Request with invalid version → returns 400 error with validation details

Configuration

Environment Variables

API Versioning Configuration

API_LATEST_VERSION=2.0 # Latest supported version
API_SUPPORTED_VERSIONS=1.0,1.1,2.0 # Comma-separated supported versions
API_V1_SUNSET_DATE=2024-12-31 # Optional sunset date for v1

Default Behavior

  • If configuration is not loaded, middleware uses sensible defaults
  • Graceful degradation for test environments
  • No breaking changes to existing deployments

Migration Guide

For API Consumers

Existing Clients (No Changes Required):

  • Continue working without modifications
  • Automatically receive v2.0 enhanced responses
  • Benefit from improved response structure

Clients Requiring v1 Format:

Add API-Version header to requests

curl -H "API-Version: 1.0" https://api.example.com/api/tasks

Clients Wanting Latest Features:

Explicitly request latest version

curl -H "API-Version: 2.0" https://api.example.com/api/tasks

For Developers

Adding New API Versions:

  1. Create new version directory: backend/src/api/routes/v3/
  2. Implement version-specific router
  3. Update API_SUPPORTED_VERSIONS configuration
  4. Add routing logic in app.ts
  5. Update version comparison logic if needed

Deprecating Old Versions:

  1. Set API_V1_SUNSET_DATE in configuration
  2. Monitor usage metrics for deprecated versions
  3. Communicate deprecation timeline to API consumers
  4. Remove deprecated versions after sunset date

Related Issue

Resolves #268 - API Versioning with Header-Based Version Negotiation

Breaking Changes

None. This implementation is fully backward compatible:

  • Existing clients without version headers receive latest version
  • v1 response format remains available via explicit version header
  • No changes to existing endpoint URLs or methods
  • Configuration uses sensible defaults when not specified

Future Enhancements

Potential improvements for future PRs:

  • Per-endpoint versioning (some endpoints in v1, others in v2)
  • Version-specific OpenAPI documentation generation
  • Analytics for API version usage
  • Automated deprecation warning emails to API consumers
  • Version-specific rate limiting
  • Gradual rollout mechanisms for new versions

Checklist

  • Tests pass
  • Lint is clean (no lint script available, TypeScript compilation successful)
  • Documentation updated (comprehensive inline documentation added)
  • Backward compatibility maintained
  • Configuration properly documented
  • Unit tests added for new functionality
  • Build process verified
  • Branch pushed to remote repository

@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

@jotel-dev is attempting to deploy a commit to the Jaja's projects Team on Vercel.

A member of the Team first needs to authorize it.

…s - Add API_DEFAULT_VERSION configuration (default: 1.0) - Update versioning middleware to use default version instead of latest - Update app.ts to default to v1 when no version is negotiated - Update tests to reflect new default behavior - This ensures existing tests continue to pass while allowing v2 opt-in
@jotel-dev

Copy link
Copy Markdown
Contributor Author

All the CI checks (backend, frontend, e2e-fullstack, smart-contracts) are passing and there's no conflict. The Vercel check is failing with "Authorization required to deploy" though — that looks like it needs a maintainer to approve/authorize the deployment on Vercel's side rather than a code fix on my end. Could someone with access take a look?
@devJaja

@devJaja
devJaja self-requested a review August 25, 2026 15:11

@devJaja devJaja 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.

Solid Implementation @jotel-dev

LGTM

@devJaja
devJaja merged commit 685e6fe into Epta-Node:main Aug 25, 2026
5 of 6 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.

feat(backend): Add API Versioning with Header-Based Version Negotiation

2 participants