Skip to content

Commit

Permalink
Merge pull request #24 from brunotot/hotfix-docs
Browse files Browse the repository at this point in the history
chore: add more docs to env
  • Loading branch information
brunotot authored Oct 1, 2024
2 parents 02412cc + 0116f46 commit 42c4f69
Show file tree
Hide file tree
Showing 87 changed files with 1,800 additions and 1,133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ table.responses-table
tbody
tr.response:not([data-code="500"]):is([data-code^="4"], [data-code^="5"])
:is(section.response-controls, div.model-example) {
display: none;
/*display: none;*/
}

.swagger-ui .highlight-code > .microlight {
Expand Down
97 changes: 37 additions & 60 deletions packages/mern-sample-app/app-node-express/src/ExpressApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,77 +17,42 @@
*
* ## How to Use
*
* To create and initialize an `ExpressApp` instance:
* ### Imports
*
* ```ts
* import { ExpressApp } from "@org/app-node-express/ExpressApp";
* ```
*
* const app = new ExpressApp();
* ### Create and initialize an `ExpressApp` instance
*
* ```ts
* const app = new ExpressApp();
* await app.init();
* await app.startListening();
* app.startListening();
* ```
*
* You can also pass custom middleware and IoC modules during instantiation:
* ### Pass custom middleware and IoC modules during instantiation
*
* ```ts
* const app = new ExpressApp({
* middleware: [customMiddleware1, customMiddleware2],
* modules: { CustomModule: CustomModuleClass },
* });
* ```
*
* To execute additional logic when the app is fully initialized, use the `onReady` callback in the `init` method:
*
* ```ts
* await app.init({}, (app) => {
* console.log('App is fully initialized and ready!');
* });
* ```
*
* ## Key Methods and Components
*
* - **init**: Initializes the server by loading middleware, routes, IoC modules, and connecting to MongoDB.
* - **startListening**: Starts the server and begins listening for incoming HTTP requests on the configured port.
* - **#initializeDatabase**: Establishes the connection to MongoDB using `MongoDatabaseService`.
* - **#initializeIoc**: Configures and loads Inversion of Control (IoC) modules for dependency injection.
* - **#initializeGlobalMiddlewares**: Adds global middleware to the Express app.
* - **#initializeSwagger**: Integrates Swagger for API documentation.
* - **#initializeErrorHandlerMiddleware**: Sets up the global error handler for catching and logging application errors.
* - **#logTable**: Logs a table of detailed server information (Node.js version, memory usage, PID, etc.) when the server starts.
*
* ## Customization
* - **Middleware**: Add or override global middleware by passing an array of middleware functions to the `ExpressApp` constructor.
* - **IoC Modules**: Inject custom services or modules by passing them to the `modules` option when initializing the `ExpressApp`.
* - **Mocking Services**: During testing, you can mock services and modules by passing a `mocks` object to the `init` method.
* - **Error Handling**: Customize error handling by extending or modifying the `#initializeErrorHandlerMiddleware` method.
*
* ## Example Usage
*
* ```ts
* import { ExpressApp } from "@org/app-node-express/ExpressApp";
*
* const app = new ExpressApp({
* middleware: [myCustomMiddleware],
* modules: { MyService: MyServiceClass },
* });
*
* await app.init();
* await app.startListening();
* ```
*
* The server will automatically load Swagger documentation and connect to MongoDB. Any errors during startup will be logged, and the process will exit gracefully if critical issues occur.
*
* @module ExpressApp
*/

import type { RouteMiddlewareFactory } from "@org/app-node-express/lib/@ts-rest";
import type { MongoClient } from "@org/app-node-express/lib/mongodb";
import type { NoArgsClass } from "@org/lib-commons";
import type { IncomingMessage, Server, ServerResponse } from "http";

import { env } from "@org/app-node-express/env";
import { initializeExpressRoutes, initializeSwagger } from "@org/app-node-express/lib/@ts-rest";
import { IocRegistry } from "@org/app-node-express/lib/bottlejs";
import { IocRegistry } from "@org/app-node-express/ioc";
import {
initializeExpressRoutes,
initializeSwagger,
TsRestRouterService,
} from "@org/app-node-express/lib/@ts-rest";
import { MongoDatabaseService } from "@org/app-node-express/lib/mongodb";
import { log, logBanner } from "@org/app-node-express/lib/winston";
import { getTypedError } from "@org/lib-api-client";
Expand All @@ -101,21 +66,22 @@ export type ExpressAppConfig = Partial<{
export class ExpressApp {
public readonly expressApp: express.Application;
public readonly port: number;
public readonly url: string;
public readonly keycloakUrl: string;
public readonly middleware: RouteMiddlewareFactory[];
public readonly modules: Record<string, NoArgsClass>;

#url: string;
#mongoClient: MongoClient;
#mockModules: Record<string, NoArgsClass>;
#httpServer: Server<typeof IncomingMessage, typeof ServerResponse>;

constructor(config: ExpressAppConfig = {}) {
this.middleware = config.middleware ?? [];
this.modules = config.modules ?? {};
this.expressApp = express();
this.keycloakUrl = env.KEYCLOAK_URL;
this.port = env.SERVER_PORT;
this.url = env.SERVER_URL;
this.#url = env.SERVER_URL;
}

public get mockModules() {
Expand All @@ -137,7 +103,7 @@ export class ExpressApp {
this.#initializeIoc(mocks);
log.info(`Initializing global middleware (${this.middleware.length})`);
this.#initializeGlobalMiddlewares();
log.info("Initializing routes");
log.info(`Initializing routes (${TsRestRouterService.getInstance().getTotalRouteCount()})`);
this.#initializeExpressRoutes();
log.info("Initializing global error handler");
this.#initializeErrorHandlerMiddleware();
Expand All @@ -148,21 +114,27 @@ export class ExpressApp {

public startListening(): void {
log.info("Server connecting...");
this.expressApp.listen(this.port, () => {
this.#httpServer = this.expressApp.listen(this.port, () => {
logBanner({
title: `[Express] ${env.SERVER_NAME} v${env.SERVER_VERSION}`,
data: {
"🟢 NodeJS": process.version,
"🏠 Env": env.SERVER_ENV,
"🔑 Keycloak": this.keycloakUrl,
"📝 Swagger": env.TS_REST_SWAGGER_ENDPOINT,
"📝 Swagger": env.SWAGGER_ENDPOINT,
"🆔 PID": `${process.pid}`,
"🧠 Memory": this.memoryUsage,
"📅 Started": new Date().toLocaleString(),
},
});
log.info(`🚀 Server listening on port ${this.port}`);
});

const oneMinuteInMsec = 60 * 1000;
this.#httpServer.setTimeout(env.SERVER_TIMEOUT_IN_MINS * oneMinuteInMsec);
this.#httpServer.on("timeout", socket => {
socket.destroy();
});
}

async #initializeDatabase() {
Expand All @@ -187,25 +159,30 @@ export class ExpressApp {
}

#initializeExpressRoutes() {
TsRestRouterService.getInstance().validateAllRoutesToBeImplemented();
initializeExpressRoutes(this.expressApp);
}

#initializeSwagger() {
initializeSwagger({
app: this.expressApp,
oauth2RedirectUrl: `${this.url}${env.TS_REST_SWAGGER_ENDPOINT}${env.TS_REST_SWAGGER_OAUTH2_REDIRECT_ENDPOINT}`,
oauth2RedirectUrl: `${this.#url}${env.SWAGGER_ENDPOINT}${env.SWAGGER_OAUTH2_REDIRECT_ENDPOINT}`,
oauth2AuthorizationUrl: `${env.KEYCLOAK_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect${env.KEYCLOAK_LOGIN_ENDPOINT}`,
oauth2TokenUrl: `${env.KEYCLOAK_URL}/realms/${env.KEYCLOAK_REALM}/protocol/openid-connect${env.KEYCLOAK_TOKEN_ENDPOINT}`,
version: env.SERVER_VERSION,
endpoint: env.TS_REST_SWAGGER_ENDPOINT,
cssPath: env.TS_REST_SWAGGER_CSS_PATH,
jsPath: env.TS_REST_SWAGGER_JS_PATH,
endpoint: env.SWAGGER_ENDPOINT,
cssPath: env.SWAGGER_CSS_PATH,
jsPath: env.SWAGGER_JS_PATH,
});
}

#initializeErrorHandlerMiddleware() {
const errorHandler: express.ErrorRequestHandler = (error: unknown, req, res, next) => {
if (res.headersSent) return next(error);
const err = getTypedError(error);
log.warn(`Headers sent before reaching main error handler`, err);
if (res.headersSent) {
log.warn(`Headers sent before reaching main error handler`, err);
return next(error);
}
res.status(err.content.status).json(err.content);
};
this.expressApp.use(errorHandler);
Expand Down
Loading

0 comments on commit 42c4f69

Please sign in to comment.