Spaces:
Paused
Paused
File size: 3,589 Bytes
c904036 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | /**
* API Versioning Middleware
* Provides versioned API routes for backward compatibility
*
* Usage:
* - Current version: /api/v1/...
* - Legacy (unversioned): /api/... (redirects to v1)
*
* Version Header Support:
* - Accept-Version: v1
* - X-API-Version: v1
*/
const { logger } = require('../Middleware/errorHandler');
// Supported API versions
const SUPPORTED_VERSIONS = ['v1'];
const DEFAULT_VERSION = 'v1';
const CURRENT_VERSION = 'v1';
/**
* Extract API version from request
* Priority: URL path > Accept-Version header > X-API-Version header > default
*/
function extractVersion(req) {
// Check URL path for version (e.g., /api/v1/users)
const pathMatch = req.path.match(/^\/api\/(v\d+)\//);
if (pathMatch && SUPPORTED_VERSIONS.includes(pathMatch[1])) {
return pathMatch[1];
}
// Check Accept-Version header
const acceptVersion = req.headers['accept-version'];
if (acceptVersion && SUPPORTED_VERSIONS.includes(acceptVersion)) {
return acceptVersion;
}
// Check X-API-Version header
const xApiVersion = req.headers['x-api-version'];
if (xApiVersion && SUPPORTED_VERSIONS.includes(xApiVersion)) {
return xApiVersion;
}
return DEFAULT_VERSION;
}
/**
* API versioning middleware
* Attaches version info to request and sets response headers
*/
function apiVersioning(req, res, next) {
const version = extractVersion(req);
// Attach version to request
req.apiVersion = version;
// Set response headers
res.setHeader('X-API-Version', version);
res.setHeader('X-API-Current-Version', CURRENT_VERSION);
// Deprecation warning for old versions (when we have v2+)
if (version !== CURRENT_VERSION) {
res.setHeader('X-API-Deprecated', 'true');
res.setHeader('Warning', '299 - "API version deprecated, please upgrade"');
}
next();
}
/**
* Create versioned router
* Wraps an Express router with version-specific path prefix
*/
function createVersionedRouter(app, version, router) {
app.use(`/api/${version}`, router);
logger.info(`Registered API ${version} routes`);
}
/**
* Legacy route redirect middleware
* Redirects unversioned /api/... to /api/v1/...
* Only for routes that don't already have a version
*/
function legacyRouteHandler(req, res, next) {
// Skip if already versioned
if (req.path.match(/^\/api\/v\d+\//)) {
return next();
}
// Skip static assets and special routes
const skipPaths = ['/api/health', '/api/docs', '/healthz'];
if (skipPaths.some(path => req.path.startsWith(path))) {
return next();
}
// For now, just continue (backward compatible)
// In future, can redirect or log deprecation warnings
req.apiVersion = DEFAULT_VERSION;
next();
}
/**
* Version check middleware for specific minimum version requirement
* @param {string} minVersion - Minimum required version (e.g., 'v2')
*/
function requireMinVersion(minVersion) {
return (req, res, next) => {
const currentVersionNum = parseInt(req.apiVersion?.replace('v', '') || '1', 10);
const requiredVersionNum = parseInt(minVersion.replace('v', ''), 10);
if (currentVersionNum < requiredVersionNum) {
return res.status(400).json({
success: false,
error: `This endpoint requires API version ${minVersion} or higher`,
currentVersion: req.apiVersion,
requiredVersion: minVersion
});
}
next();
};
}
module.exports = {
apiVersioning,
createVersionedRouter,
legacyRouteHandler,
requireMinVersion,
extractVersion,
SUPPORTED_VERSIONS,
DEFAULT_VERSION,
CURRENT_VERSION
};
|