modarb-be / src /routes.ts
moahmedwafy's picture
update: swagger doc
5d056c4
import { Router, Express } from "express";
import * as glob from "glob";
import path from "path";
import swaggerUi from "swagger-ui-express";
import { BaseController } from "./lib/controllers/controller.base";
import { validationErrorHandler } from "./helpers/validation.helper";
import { JsonResponse } from "@lib/responses/json-response";
import { errorHandlerMiddleware } from "middlewares/error-handler.middleware";
import { swaggerRegistry } from "@lib/swagger/swagger";
/**
* Sets the routes for the Express app.
*
* @param app - The Express app.
*/
export const setAppRoutes = async (app: Express) => {
const mainRouter = Router();
await importControllers(mainRouter);
await setCustomRoutes(mainRouter);
app.use("/api/v1", mainRouter);
};
/* custom routes */
/**
* Sets custom routes for the router.
*
* @param router - The router object to set the routes on.
*/
const setCustomRoutes = async (router: Router) => {
// Health check route
router.get("/health", (_req: any, res: any) => {
JsonResponse.success(
{
message: "Server is up!",
data: { success: true },
},
res
);
});
// Validation error handler
router.use(validationErrorHandler);
// docs
router.use(
"/docs",
swaggerUi.serve,
swaggerUi.setup(swaggerRegistry.generateSwaggerDocument(), {
customCss: ".swagger-ui .topbar { display: none }",
customSiteTitle: "API Documentation",
swaggerOptions: {
layout: "StandaloneLayout",
deepLinking: true,
docExpansion: "none",
filter: true,
tagsSorter: "alpha",
operationsSorter: "alpha",
showMutatedRequest: true,
showMutatedResponse: true,
showRequestDuration: true,
persistAuthorization: true,
displayRequestDuration: true,
withCredentials: true,
},
})
);
// Invalid URL handler
router.all("*", (req: any, res: any) => {
JsonResponse.error(
{
error: "Invalid URL!",
status: 404,
},
res
);
});
// Error handler
router.use(errorHandlerMiddleware);
};
/* importing all controllers */
/**
* Finds all controller files in the project.
* @returns An array of strings representing the absolute paths of the controller files.
*/
const findControllerFiles = (): string[] => {
const controllersPath = path
.relative(process.cwd(), path.join(__dirname, "**/*.controller.{ts,js}"))
.replace(/\\/g, "/");
return glob.sync(controllersPath, {}).map((file) => {
return path.resolve(file);
});
};
/**
* Imports controller classes from files, sets up routes for each controller,
* and adds them to the provided router.
*
* @param router - The router to add the routes to.
*/
const importControllers = async (router: Router) => {
const files = findControllerFiles();
console.log("importing controllers...");
await Promise.all(
files.map(async (file) => {
const controllerClass = await importController(file);
if (!controllerClass) return;
const controller: BaseController = new (controllerClass as any)();
controller.setRoutes();
router.use(controller.prefix, controller.router);
})
);
console.log("controllers imported!");
};
/**
* Imports a module from a file and returns the first controller that extends BaseController.
* @param file - The path to the file containing the module.
* @returns The first controller that extends BaseController.
*/
const importController = async (file: string) => {
const controllers = Object.values(await import(file));
return controllers.find(
(controller: { prototype: typeof BaseController }) =>
controller.prototype instanceof BaseController
) as typeof BaseController;
};