express-swagger-autogen
Version:
A library that auto generates swagger docs to your endpoints from express.
215 lines (214 loc) • 12.5 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StatusCodes = exports.ExpressSwaggerAutogenValidationError = exports.Documentation = void 0;
exports.default = expressSwaggerAutogen;
require("reflect-metadata");
var express_1 = require("express");
var http_status_codes_1 = require("http-status-codes");
Object.defineProperty(exports, "StatusCodes", { enumerable: true, get: function () { return http_status_codes_1.StatusCodes; } });
var swagger_ui_express_1 = __importDefault(require("swagger-ui-express"));
var zod_1 = __importDefault(require("zod"));
var zod_openapi_1 = require("zod-openapi");
var errors_1 = require("./errors");
Object.defineProperty(exports, "ExpressSwaggerAutogenValidationError", { enumerable: true, get: function () { return errors_1.ExpressSwaggerAutogenValidationError; } });
var utils_1 = require("./utils");
var decorator_1 = require("./decorator");
Object.defineProperty(exports, "Documentation", { enumerable: true, get: function () { return decorator_1.Documentation; } });
var expressVersion = require("express/package.json").version;
/**
* Generates Swagger documentation for an Express application.
*
* @param {Router} router - The Express Router instance.
* @param {ExpressSwaggerAutogenDocsOptions} [options] - Optional configuration options.
* @param {Partial<OpenAPI3>} [options.setup] - Custom OpenAPI3 setup for Swagger UI.
* @param {string} [options.basePath] - Base path to prepend to all endpoints.
* @param {string} [options.endpoint] - The endpoint where the Swagger UI will be served. Defaults to "/documentation".
* @param {boolean} [options.validatePaths] - Whether to validate the paths defined manually in options.setup against the actual endpoints in the router. If true, it will throw an error if any path or method does not exist in the router endpoints. If false it will just warn in console.
* @param {boolean} [options.disableLogger] - Whether to disable the logger. Defaults to false.
* @param {boolean} [options.deprecateNotFoundPaths] - Whether to deprecate paths that are not found in the router. Defaults to true.
* @param {boolean} [options.includeCustomQueryParams] - Whether to include custom query parameters in the documentation. Defaults to false.
*/
function expressSwaggerAutogen(router, options) {
var _a, _b;
// Validate the router instance only if Express version is >= 5
var majorVersion = parseInt(expressVersion.split(".")[0], 10);
if (majorVersion >= 5 && !(router instanceof express_1.Router)) {
throw new Error("The first argument must be an instance of express.Router.");
}
// Set default options
options = __assign(__assign({}, options), { endpoint: (options === null || options === void 0 ? void 0 : options.endpoint) || "/documentation" });
// List all routes in the router
var list = utils_1.ExpressSwaggerAutogenUtils.listRoutes(router);
// If basePath is provided, prepend it to all paths
if (options === null || options === void 0 ? void 0 : options.basePath) {
list = list.map(function (endpoint) {
return __assign(__assign({}, endpoint), { path: "".concat(options === null || options === void 0 ? void 0 : options.basePath).concat(endpoint.path) });
});
}
// Validate paths if validatePaths is true
if ((_a = options === null || options === void 0 ? void 0 : options.setup) === null || _a === void 0 ? void 0 : _a.paths) {
Object.entries(options.setup.paths).forEach(function (_a) {
var path = _a[0], setup = _a[1];
var normalizedPath = utils_1.ExpressSwaggerAutogenUtils.normalizeSwaggerToExpressPath(path);
Object.keys(setup).forEach(function (method) {
var endpoint = list.find(function (endpoint) { return endpoint.path === normalizedPath && endpoint.method === method.toUpperCase(); });
if (!endpoint) {
var message = "Endpoint \"".concat(method.toUpperCase(), " ").concat(normalizedPath, "\" from \"").concat(path, "\" at setup.paths does not exist in the router.");
var _a = options.deprecateNotFoundPaths, deprecateNotFoundPaths = _a === void 0 ? true : _a;
var hasDeprecated = "deprecated" in setup[method];
if (hasDeprecated && setup[method].deprecated) {
return;
}
if (!hasDeprecated) {
setup[method].deprecated = deprecateNotFoundPaths;
}
if (!(options === null || options === void 0 ? void 0 : options.disableLogger)) {
utils_1.ExpressSwaggerAutogenUtils.logger(message);
}
if (options === null || options === void 0 ? void 0 : options.validatePaths) {
throw new errors_1.ExpressSwaggerAutogenValidationError(message);
}
}
});
});
}
// Setup paths for documentation
var paths = {};
list.forEach(function (endpoint) {
var _a, _b, _c, _d, _e, _f, _g;
var method = endpoint.method, path = endpoint.path, handlers = endpoint.handlers;
var documentation = handlers
.map(function (handler) { return Reflect.getMetadata(utils_1.ExpressSwaggerAutogenUtils.METADATA_KEY, handler); })
.find(function (doc) { return doc; });
if (!paths[path]) {
paths[path] = {};
}
// Documentation for parameters
var pathParams = utils_1.ExpressSwaggerAutogenUtils.extractPathParams(path);
var parameters = [];
parameters.push.apply(parameters, pathParams.map(function (param) {
return zod_1.default.string().meta({
param: {
name: param,
in: "path",
required: true,
},
});
}));
if (((_a = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _a === void 0 ? void 0 : _a.params) || (documentation === null || documentation === void 0 ? void 0 : documentation.parameters)) {
if (documentation === null || documentation === void 0 ? void 0 : documentation.parameters) {
parameters.push.apply(parameters, documentation === null || documentation === void 0 ? void 0 : documentation.parameters);
}
else if ((_b = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _b === void 0 ? void 0 : _b.params) {
parameters.push.apply(parameters, (_c = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _c === void 0 ? void 0 : _c.params);
}
}
else if (options === null || options === void 0 ? void 0 : options.includeCustomQueryParams) {
parameters.push(zod_1.default
.object({
search: zod_1.default.string().optional().default("How to use auto generate api documentation?"),
})
.optional()
.meta({
param: {
in: "query",
name: "queryParams",
description: "Custom query parameters",
style: "form",
explode: true,
},
}));
}
// Documentation for request body
var requestBody = documentation === null || documentation === void 0 ? void 0 : documentation.requestBody;
if (!requestBody && ((_d = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _d === void 0 ? void 0 : _d.requestBody)) {
requestBody = {
required: true,
content: {
"application/json": {
schema: (_e = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _e === void 0 ? void 0 : _e.requestBody,
},
},
};
}
if (!requestBody && ["POST", "PUT", "PATCH"].includes(method)) {
requestBody = {
required: false,
content: {
"application/json": {
schema: zod_1.default.object({}),
},
},
};
}
// Documentation for responses
var responses = documentation === null || documentation === void 0 ? void 0 : documentation.responses;
if (!responses && ((_f = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _f === void 0 ? void 0 : _f.responses)) {
responses = Object.fromEntries(Object.entries((_g = documentation === null || documentation === void 0 ? void 0 : documentation.zod) === null || _g === void 0 ? void 0 : _g.responses).map(function (_a) {
var status = _a[0], schema = _a[1];
return [
status,
{
description: (schema === null || schema === void 0 ? void 0 : schema.description) || (0, http_status_codes_1.getReasonPhrase)(status),
content: !["null", "undefined", "void"].includes(schema.def.type) && {
"application/json": {
schema: schema,
},
},
},
];
}));
}
if (!responses || Object.keys(responses).length === 0) {
responses = {
200: {
description: "Successful",
},
};
}
// Documentation tags
var tags = [];
if (documentation === null || documentation === void 0 ? void 0 : documentation.tags) {
tags = documentation === null || documentation === void 0 ? void 0 : documentation.tags;
}
else {
tags = [utils_1.ExpressSwaggerAutogenUtils.extractFirstPathName((options === null || options === void 0 ? void 0 : options.basePath) ? path.slice(options.basePath.length) : path)];
}
// Assign the documentation to the path and method
paths[path][method.toLowerCase()] = __assign(__assign({}, documentation), { tags: tags, parameters: parameters, requestBody: requestBody, responses: responses });
});
// Create the OpenAPI document
var defaultSetup = {
openapi: "3.0.0",
info: {
title: process.env.npm_package_name,
version: process.env.npm_package_version,
description: "API documentation generated by [Express Swagger Autogen](https://github.com/CarlosSLoureiro/express-swagger-autogen)",
},
};
var setup = __assign(__assign({}, ((options === null || options === void 0 ? void 0 : options.setup) ? utils_1.ExpressSwaggerAutogenUtils.merge(defaultSetup, options.setup) : defaultSetup)), { paths: ((_b = options === null || options === void 0 ? void 0 : options.setup) === null || _b === void 0 ? void 0 : _b.paths) ? utils_1.ExpressSwaggerAutogenUtils.merge(paths, options.setup.paths) : paths });
// Normalize paths to Swagger style
setup.paths = Object.fromEntries(Object.entries(setup.paths).map(function (_a) {
var path = _a[0], value = _a[1];
return [utils_1.ExpressSwaggerAutogenUtils.normalizeExpressToSwaggerPath(path), value];
}));
// Serve the Swagger UI
router.use(options.endpoint, swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup((0, zod_openapi_1.createDocument)(setup)));
if (!(options === null || options === void 0 ? void 0 : options.disableLogger)) {
utils_1.ExpressSwaggerAutogenUtils.logger("Swagger documentation available at endpoint \"".concat(options.endpoint, "\""));
}
}