OpenAPI
master.useOpenApi() is ASP.NET Core’s AddOpenApi() / MapOpenApi() plus a Swagger UI page. The document is derived from routes, typed constraints, static schemas, authorize, registered auth schemes, and output-cache policies — there is nothing to keep in sync by hand.
Setup#
const server = master.setupServer('http');
master.useOpenApi({
path: '/openapi.json', // default
ui: '/docs', // Swagger UI page (true = '/docs', false = none); CDN-loaded in the browser
title: 'Items API', version: '2.0.0', description: '…',
servers: [{ url: 'https://api.example.com' }],
filter: ({ controller }) => controller !== 'internal', // hide routes
transform: (doc) => doc, // last-chance edits
});
await master.start(server);
master.openApi.document(); // the object, e.g. for tests or a build stepOptions: path, ui, title, version, description, servers, tags, filter, transform. Open /docs in a browser for the interactive UI, or fetch /openapi.json for tooling.
What is derived#
| Source | OpenAPI |
|---|---|
router.route('/items/:id(int)', 'items#show', 'get') | paths['/items/{id}'].get, path parameter id: integer; operationId: items_show, tags: ['items'] |
router.resources('tags', { only }) | the REST operations |
static schemas = { create: { body, query, headers, route } } | requestBody (JSON Schema from the validation rules), query/header/path parameters, 400 ValidationProblemDetails |
route { authorize } / static authorize / { allowAnonymous } | security: [{ bearer: ['role:admin', 'policy:Senior'] }], 401/403 ProblemDetails; anonymous → security: [] |
master.auth.addJwtBearer / addCookie / custom | components.securitySchemes (http bearer JWT, apiKey cookie, apiKey header) |
static openapi = { action: { summary, description, tags, responses, deprecated, operationId } } | operation metadata (responses values: a string description or a full response object) |
{ outputCache } | x-output-cache extension |
master.map.get(...) minimal APIs | documented like controller routes, under the minimal tag |
Rules → JSON Schema#
string (minLength/maxLength/pattern), email/url/uuid/date/datetime (formats), number/integer (minimum/maximum), boolean, array (items/minItems/maxItems), object (properties/required), enum, default, nullable. See Model Binding for the rule set.
Customizing#
Per-operation metadata lives next to the action in static openapi; the openapi option does the same for minimal APIs.
export default class ItemsController {
static schemas = {
create: { body: { name: 'string!', price: { type: 'number', min: 0 } } },
index: { query: { page: { type: 'integer', default: 1, min: 1 } } },
};
static authorize = { '*': true, index: false, show: false };
// operation metadata — the equivalent of [EndpointSummary] / [ProducesResponseType]
static openapi = {
create: { summary: 'Create an item', tags: ['catalog'], responses: { 201: 'Created', 409: 'Duplicate name' } },
index: { summary: 'List items', deprecated: false, operationId: 'listItems' },
show: { responses: { 200: { description: 'The item', content: { 'application/json': { schema: { $ref: '#/components/schemas/Item' } } } } } },
};
}filter drops routes from the document; transform receives the finished document for last-chance edits — add shared component schemas, contact info, or anything the generator does not know about.
master.useOpenApi({
title: 'Items API', version: '2.0.0',
transform: (doc) => {
doc.components.schemas.Item = { type: 'object', properties: { id: { type: 'integer' }, name: { type: 'string' } } };
doc.info.contact = { email: 'api@example.com' };
return doc;
},
});
// regenerate after runtime changes (e.g. routes mapped after start)
master.openApi.invalidate();ui: false if your CSP forbids external scripts and point your own UI at /openapi.json.