Skip to content

Commit

Permalink
chore: refactor to internal module pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
brunotot committed Apr 21, 2024
1 parent 3a072ef commit 1470ea7
Show file tree
Hide file tree
Showing 93 changed files with 1,259 additions and 591 deletions.
4 changes: 3 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
},
"plugins": ["@typescript-eslint"],
"ignorePatterns": ["node_modules/", "dist/", "logger.js", "docs/"],
"rules": {}
"rules": {
"@typescript-eslint/consistent-type-imports": "error"
}
}
2 changes: 1 addition & 1 deletion .github/workflows/typedoc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: MERN Sample App - TypeDoc [GH Pages]
name: MERN Sample App - TypeDoc

on:
push:
Expand Down
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
.eslintrc
docs/
docs/
./packages/backend/src/internal.ts
packages/backend/src/**/index.ts
**/internal.ts
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "backend:start"],
"presentation": { "group": "1" }
//"runtimeArgs": ["--loader", "ts-node/esm", "--experimental-specifier-resolution=node"]
},
{
"name": "🖼️ Launch Frontend",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"prepare": "husky",
"root:test": "echo \"Error: no test specified\"",
"root:typedoc": "typedoc --options typedoc/typedoc.json && bash typedoc/scripts/typedoc-sanitize.sh",
"root:lint": "npx eslint .",
"root:lint": "npx eslint . --fix",
"backend:start": "npm run start --prefix packages/backend",
"backend:build": "npm run build --prefix packages/backend",
"frontend:dev": "npm run dev --prefix packages/frontend",
Expand Down
7 changes: 4 additions & 3 deletions packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"build": "rm -rf dist && npm run compile:ts",
"compile:ts": "tsc && npm run tsc-alias",
"start": "export PACKAGE_JSON_VERSION=$(grep -o '\"version\": *\"[^\"]*\"' package.json | awk -F'\"' '{print $4}') && node --no-warnings --loader ts-node/esm --experimental-specifier-resolution=node ./dist/backend/src/main.js",
"tsc-alias": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json"
"tsc-alias": "tsc --project tsconfig.json && tsc-alias -p tsconfig.json",
"dependency-test": "npx madge --circular --extensions ts ./src/index.ts"
},
"keywords": [],
"author": "Bruno Tot",
Expand All @@ -18,14 +19,13 @@
"@org/shared": "file:../shared/src",
"@tsvdec/core": "^2.0.11",
"@tsvdec/decorators": "^1.0.7",
"@zodyac/zod-mongoose": "^1.1.2",
"bcrypt": "^5.1.1",
"bottlejs": "^2.0.1",
"express": "^4.18.2",
"express-rate-limit": "^7.2.0",
"http-status": "^1.7.4",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.2.0",
"mongoose": "^8.3.2",
"openapi3-ts": "^4.3.1",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
Expand All @@ -50,6 +50,7 @@
"dotenv": "^16.4.5",
"helmet": "^7.1.0",
"hpp": "^0.2.3",
"madge": "^7.0.0",
"morgan": "^1.10.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
Expand Down
5 changes: 4 additions & 1 deletion packages/backend/src/@types/express.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HttpStatusNumeric } from "../config";
import type { HttpStatusNumeric } from "@internal";

declare global {
namespace Express {
Expand All @@ -9,5 +9,8 @@ declare global {
metadata?: Record<string, unknown>,
) => never;
}
export interface Request {
decoratorContext: DecoratorContext;
}
}
}
1 change: 0 additions & 1 deletion packages/backend/src/@types/index.d.ts

This file was deleted.

3 changes: 3 additions & 0 deletions packages/backend/src/@types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type * from "./express";
export type * from "./swagger";

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TODO } from "@org/shared";
import HttpStatus from "http-status";
import swaggerJsdoc from "swagger-jsdoc";
import type HttpStatus from "http-status";
import type { oas31 } from "openapi3-ts";
import type { ReferenceObject, SchemaObject } from "openapi3-ts/oas31";
import type swaggerJsdoc from "swagger-jsdoc";

export type HttpStatusConverter<T> = {
[K in keyof T]: T[K] extends number ? T[K] : never;
Expand Down Expand Up @@ -75,9 +76,9 @@ export type SwaggerResponse = Partial<
>
>;

export type SwaggerEntitySchema = TODO;
export type SwaggerSchemaObject = SchemaObject | ReferenceObject;

export type SwaggerRequestContent = Record<string, { schema: SwaggerEntitySchema }>;
export type SwaggerRequestContent = Record<string, { schema: SwaggerSchemaObject }>;

export type SwaggerRequestBody = {
description?: string;
Expand All @@ -88,4 +89,5 @@ export type SwaggerRequestBody = {
export type SwaggerDefinition = swaggerJsdoc.SwaggerDefinition & {
tags: SwaggerTag[];
paths: SwaggerPaths;
components: oas31.ComponentsObject;
};
43 changes: 24 additions & 19 deletions packages/backend/src/App.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { $BackendAppConfig, mongoConnect, registerSwagger, startupLog } from "@config";
import { registerRoutes } from "@decorators";
import { GLOBAL_MIDDLEWARES } from "@infrastructure";
import express from "express";

import {
GLOBAL_MIDDLEWARES,
VAR_ZOD_ENVIRONMENT,
mongoConnect,
registerRoutes,
registerSwagger,
startupLog,
} from "@internal";

export class App {
public readonly app: express.Application;
public readonly env: string;
Expand All @@ -11,13 +17,19 @@ export class App {
public readonly url: string;

constructor() {
this.#initializeUncaughtExceptionHandler();

this.app = express();
this.env = $BackendAppConfig.env.NODE_ENV;
this.port = $BackendAppConfig.env.PORT;
this.env = VAR_ZOD_ENVIRONMENT.NODE_ENV;
this.port = VAR_ZOD_ENVIRONMENT.PORT;
this.swaggerPath = "api-docs";
this.url = $BackendAppConfig.url;
function buildUrl() {
const domain =
VAR_ZOD_ENVIRONMENT.NODE_ENV === "production"
? `https://${process.env.RAILWAY_PUBLIC_DOMAIN}`
: "http://localhost";

return `${domain}:${VAR_ZOD_ENVIRONMENT.PORT}`;
}
this.url = buildUrl();

this.#initializeDatabase();
this.#initializeMiddlewares();
Expand All @@ -28,15 +40,14 @@ export class App {
public listen() {
this.app.listen(this.port, () => {
startupLog({
title: `[Express] MERN Sample App v${$BackendAppConfig.env.PACKAGE_JSON_VERSION}`,
title: `[Express] MERN Sample App v${VAR_ZOD_ENVIRONMENT.PACKAGE_JSON_VERSION}`,
data: {
"🟢 NodeJS": process.version,
"📦 Database": $BackendAppConfig.databaseConnectionParams.dbName,
"🚀 App": this.url,
"📝 Swagger": `${this.url}/${this.swaggerPath}`,
"🏠 Env": this.env,
"🚀 Port": this.port,
"📝 Swagger": `/${this.swaggerPath}`,
"🆔 PID": `${process.pid}`,
"🧠 Memory": `${Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100} MB`,
"🏠 Env": this.env,
"📅 Started": new Date().toLocaleString(),
},
});
Expand All @@ -58,10 +69,4 @@ export class App {
#initializeSwagger() {
registerSwagger(this.app, this.swaggerPath);
}

#initializeUncaughtExceptionHandler() {
process.on("uncaughtException", err => {
console.error("Uncaught Exception:", err);
});
}
}
27 changes: 0 additions & 27 deletions packages/backend/src/config/BackendAppConfig.ts

This file was deleted.

15 changes: 0 additions & 15 deletions packages/backend/src/config/index.ts

This file was deleted.

4 changes: 0 additions & 4 deletions packages/backend/src/config/ioc/index.ts

This file was deleted.

7 changes: 0 additions & 7 deletions packages/backend/src/config/ioc/scanner.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { existsSync, mkdirSync } from "fs";
import { join } from "path";
import winston from "winston";
import winstonDaily from "winston-daily-rotate-file";
import { $BackendAppConfig } from "../BackendAppConfig";

const logDir: string = join(getDirname(), $BackendAppConfig.env.LOG_DIR);
import { VAR_ZOD_ENVIRONMENT } from "@internal";

const logDir: string = join(getDirname(), VAR_ZOD_ENVIRONMENT.LOG_DIR);

if (!existsSync(logDir)) {
mkdirSync(logDir);
Expand Down
1 change: 0 additions & 1 deletion packages/backend/src/config/logger/index.ts

This file was deleted.

91 changes: 91 additions & 0 deletions packages/backend/src/config/swagger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import type { OpenApiZodAny } from "@anatine/zod-openapi";
import type { Class } from "@org/shared";
import type express from "express";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";

import { RouteDecoratorManager, VAR_ZOD_ENVIRONMENT, generateSwaggerSchema } from "@internal";
import type { SwaggerDefinition, SwaggerPath, SwaggerRequestMapping, SwaggerTag } from "@types";

const DEFAULT_SWAGGER_DEFINITION: SwaggerDefinition = {
openapi: "3.0.0",
info: {
title: "REST API",
license: {
name: "MIT",
url: "https://spdx.org/licenses/MIT.html",
},
termsOfService: "http://swagger.io/terms/",
contact: {
email: "",
name: "",
url: "",
},
version: VAR_ZOD_ENVIRONMENT.PACKAGE_JSON_VERSION,
description: "This is a dynamically generated Swagger API documentation",
},
components: {
schemas: {},
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
},
security: [{ bearerAuth: [] }],
tags: [],
paths: {},
};

const definition: SwaggerDefinition = DEFAULT_SWAGGER_DEFINITION;
const controllerClasses: Class[] = [];

export function registerSchema(name: string, schema: OpenApiZodAny) {
const description = name;
if (!description) throw new Error("Schema must have a description");
definition.components.schemas![description] = generateSwaggerSchema(schema);
}

export function registerTag(tagData: SwaggerTag & { constructor: Class }) {
if (definition.tags.some(t => t.name === tagData.name)) return;
const { constructor, ...tag } = tagData;
controllerClasses.push(constructor);
definition.tags.push(tag);
}

export function registerPath(
path: string,
requestMapping: SwaggerRequestMapping,
data: SwaggerPath,
) {
if (!definition.paths[path]) definition.paths[path] = {};
definition.paths[path][requestMapping] = data;
}

export function registerPaths() {
controllerClasses.forEach(controllerClass => {
const meta = RouteDecoratorManager.from(controllerClass).value;
meta.routes.forEach(route => {
const fullPath = `${meta.basePath}${route.path}`;
const swagger = route.swagger ?? {};
swagger.tags = [String(controllerClass.name)];
registerPath(fullPath, route.method, swagger);
});
});
}

export function buildSpec(): object {
registerPaths();
return swaggerJsdoc({ definition, apis: [] });
}

export function registerSwagger(app: express.Application, path: string) {
const swaggerSpec = buildSpec();
app.use(`/${path}`, swaggerUi.serve, swaggerUi.setup(swaggerSpec));
app.get(`/${path}.json`, (_req, res) => {
res.setHeader("Content-Type", "application/json");
res.send(swaggerSpec);
});
}
2 changes: 0 additions & 2 deletions packages/backend/src/config/swagger/index.ts

This file was deleted.

Loading

0 comments on commit 1470ea7

Please sign in to comment.