MasterController
API Versioning
Serve several versions of a path side by side and tell clients which ones exist.
master.useApiVersioning() mirrors Asp.Versioning: configurable version readers, apiVersion / deprecated on routes, groups, and minimal APIs, the requested version on this.apiVersion, api-supported-versions headers, and 400 ProblemDetails when a client asks for something you do not serve.
Setup#
javascript
master.useApiVersioning({
defaultVersion: '1.0',
assumeDefaultWhenUnspecified: true,
reportApiVersions: true,
readers: ['query:api-version', 'header:x-api-version', 'media-type:v', 'url-segment'],
});
router.route('/items', 'itemsV1#index', 'get', { apiVersion: '1.0', deprecated: true }); // [ApiVersion("1.0")] [Obsolete]
router.route('/items', 'itemsV2#index', 'get', { apiVersion: ['2.0', '3.0'] }); // [ApiVersion("2.0")] [ApiVersion("3.0")]
router.group('/api', { apiVersion: '2.0' }, (g) => g.resources('orders')); // group default
router.route('/v:version/reports', 'reports#index', 'get'); // url-segment: /v2/reports -> ctx.apiVersion '2.0'defaultVersion('1.0') — used when the request names no version andassumeDefaultWhenUnspecifiedistrue(the default).readers— any of'query:<name>','header:<name>','media-type:<param>'(Accept: application/json;v=2.0),'url-segment'(/v2/..., with a:versionroute parameter).reportApiVersions(true) — addsapi-supported-versions/api-deprecated-versionsto versioned responses.
Declaring versions#
{ apiVersion: '2.0' | ['2.0', '3.0'], deprecated: true } on router.route(), router.group() (inherited default), router.resources(), and master.map.*. The requested version is available as this.apiVersion in actions and ctx.apiVersion in middleware.
javascript
export default class ItemsV2Controller {
async index() {
this.logger.info('serving {version}', { version: this.apiVersion }); // '2.0' or '3.0'
this.ok(await this.db.Item.toList());
}
}
// minimal APIs take the same options
master.map.get('/ping', () => ({ pong: true }), { apiVersion: '2.0' });Resolution rules#
- Readers run in order; the first that finds a version wins. A malformed value → 400
Invalid API version(ProblemDetails). - A versioned route serves only its versions. If the path exists but no route matches the requested version → 400
Unsupported API versionwithapi-supported-versions(ASP.NET behaviour). - Unversioned routes serve any version.
on the wire
GET /items?api-version=2.0
GET /items (x-api-version: 3.0)
GET /items (Accept: application/json;v=2.0)
GET /v2/reports
# responses (reportApiVersions: true)
api-supported-versions: 1.0, 2.0, 3.0
api-deprecated-versions: 1.0
# no route for the requested version
HTTP/1.1 400 Bad Request
content-type: application/problem+json
api-supported-versions: 1.0, 2.0, 3.0
{ "title": "Unsupported API version", "status": 400, ... } Deprecating a version
Keep the old route registered with
deprecated: true: it still serves, but every versioned response advertises it in api-deprecated-versions so clients can migrate before you remove it. Mark the matching operations deprecated in static openapi so the OpenAPI document says the same.