@ifecodes/backend-template 1.1.9 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +423 -383
  2. package/bin/cli.js +1276 -964
  3. package/bin/lib/microservice-config.js +155 -150
  4. package/bin/lib/prompts.js +277 -241
  5. package/bin/lib/readme-generator.js +364 -329
  6. package/bin/lib/service-setup.js +901 -684
  7. package/package.json +64 -55
  8. package/template/base/js/.eslintrc.json +10 -13
  9. package/template/base/js/.prettierrc +7 -7
  10. package/template/base/js/eslint.config.js +33 -31
  11. package/template/base/js/package.json +29 -28
  12. package/template/base/js/src/app.js +20 -18
  13. package/template/base/js/src/config/db.js +8 -8
  14. package/template/base/js/src/config/env.js +44 -14
  15. package/template/base/js/src/config/index.js +7 -7
  16. package/template/base/js/src/docs/index.js +5 -0
  17. package/template/base/js/src/docs/route-registry.js +63 -0
  18. package/template/base/js/src/middlewares/error-handler.middleware.js +22 -19
  19. package/template/base/js/src/middlewares/index.js +15 -11
  20. package/template/base/js/src/middlewares/method-not-allowed.middleware.js +19 -13
  21. package/template/base/js/src/middlewares/not-found.middleware.js +13 -10
  22. package/template/base/js/src/middlewares/observability.middleware.js +24 -0
  23. package/template/base/js/src/middlewares/root.middleware.js +18 -16
  24. package/template/base/js/src/middlewares/validation.middleware.js +39 -0
  25. package/template/base/js/src/modules/index.js +8 -8
  26. package/template/base/js/src/modules/v1/health/health.controller.auth.js +29 -0
  27. package/template/base/js/src/modules/v1/health/health.controller.js +21 -21
  28. package/template/base/js/src/modules/v1/health/health.route.js +74 -9
  29. package/template/base/js/src/modules/v1/health/index.js +5 -5
  30. package/template/base/js/src/modules/v1/index.js +8 -8
  31. package/template/base/js/src/routes.js +23 -16
  32. package/template/base/js/src/server.js +18 -18
  33. package/template/base/js/src/utils/http-error.js +74 -74
  34. package/template/base/js/src/utils/index.js +28 -28
  35. package/template/base/js/src/utils/logger.js +57 -67
  36. package/template/base/ts/.eslintrc.json +13 -17
  37. package/template/base/ts/.prettierrc +7 -7
  38. package/template/base/ts/eslint.config.js +33 -33
  39. package/template/base/ts/package.json +41 -39
  40. package/template/base/ts/src/app.ts +20 -18
  41. package/template/base/ts/src/config/db.ts +4 -4
  42. package/template/base/ts/src/config/env.ts +40 -10
  43. package/template/base/ts/src/config/index.ts +2 -2
  44. package/template/base/ts/src/docs/index.ts +3 -0
  45. package/template/base/ts/src/docs/route-registry.ts +98 -0
  46. package/template/base/ts/src/middlewares/error-handler.middleware.ts +4 -1
  47. package/template/base/ts/src/middlewares/index.ts +6 -4
  48. package/template/base/ts/src/middlewares/method-not-allowed.middleware.ts +23 -18
  49. package/template/base/ts/src/middlewares/not-found.middleware.ts +10 -8
  50. package/template/base/ts/src/middlewares/observability.middleware.ts +25 -0
  51. package/template/base/ts/src/middlewares/root.middleware.ts +16 -14
  52. package/template/base/ts/src/middlewares/validation.middleware.ts +46 -0
  53. package/template/base/ts/src/modules/index.ts +8 -8
  54. package/template/base/ts/src/modules/v1/health/health.controller.auth.ts +26 -0
  55. package/template/base/ts/src/modules/v1/health/health.controller.ts +18 -18
  56. package/template/base/ts/src/modules/v1/health/health.route.ts +68 -9
  57. package/template/base/ts/src/modules/v1/health/index.ts +1 -1
  58. package/template/base/ts/src/modules/v1/index.ts +8 -8
  59. package/template/base/ts/src/routes.ts +23 -15
  60. package/template/base/ts/src/server.ts +19 -19
  61. package/template/base/ts/src/utils/http-error.ts +63 -63
  62. package/template/base/ts/src/utils/index.ts +14 -14
  63. package/template/base/ts/src/utils/logger.ts +58 -68
  64. package/template/base/ts/tsconfig.json +21 -21
  65. package/template/features/auth/argon2/inject.js +50 -50
  66. package/template/features/auth/base/health-openapi.ts +62 -0
  67. package/template/features/auth/base/inject.js +174 -172
  68. package/template/features/auth/bcrypt/inject.js +40 -40
  69. package/template/features/auth/models/index.ts +1 -1
  70. package/template/features/auth/models/user.model.js +24 -24
  71. package/template/features/auth/models/user.model.ts +28 -28
  72. package/template/features/auth/modules/auth.controller.js +21 -21
  73. package/template/features/auth/modules/auth.controller.ts +28 -20
  74. package/template/features/auth/modules/auth.routes.js +89 -10
  75. package/template/features/auth/modules/auth.routes.ts +86 -11
  76. package/template/features/auth/modules/auth.service.js +29 -29
  77. package/template/features/auth/modules/auth.service.ts +38 -38
  78. package/template/features/auth/modules/index.js +1 -1
  79. package/template/features/auth/modules/index.ts +1 -1
  80. package/template/features/auth/utils/hash.ts +20 -20
  81. package/template/features/auth/utils/jwt.js +12 -12
  82. package/template/features/auth/utils/jwt.ts +15 -15
  83. package/template/features/cors/inject.js +14 -13
  84. package/template/features/helmet/inject.js +7 -6
  85. package/template/features/morgan/inject.js +8 -7
  86. package/template/features/rate-limit/inject.js +7 -6
  87. package/template/gateway/js/app.js +42 -42
  88. package/template/gateway/js/inject.js +33 -33
  89. package/template/gateway/js/server.js +19 -19
  90. package/template/gateway/ts/app.ts +43 -43
  91. package/template/gateway/ts/inject.js +33 -33
  92. package/template/gateway/ts/server.ts +19 -19
  93. package/template/microservice/docker/docker-compose.yml +5 -5
  94. package/template/microservice/nodocker/pm2.config.js +3 -3
package/package.json CHANGED
@@ -1,55 +1,64 @@
1
- {
2
- "name": "@ifecodes/backend-template",
3
- "version": "1.1.9",
4
- "description": "Production-ready Express + TypeScript/JavaScript backend generator with optional features and microservice support",
5
- "bin": {
6
- "ifecodes-template": "bin/cli.js"
7
- },
8
- "type": "module",
9
- "files": [
10
- "bin",
11
- "template"
12
- ],
13
- "scripts": {
14
- "test": "echo \"Error: no test specified\" && exit 1"
15
- },
16
- "repository": {
17
- "type": "git",
18
- "url": "git+https://github.com/ALADETAN-IFE/backend-template.git"
19
- },
20
- "bugs": {
21
- "url": "https://github.com/ALADETAN-IFE/backend-template/issues"
22
- },
23
- "homepage": "https://backend-template-generator.vercel.app",
24
- "publishConfig": {
25
- "access": "public"
26
- },
27
- "keywords": [
28
- "cli",
29
- "template",
30
- "boilerplate",
31
- "typescript",
32
- "javascript",
33
- "backend",
34
- "express",
35
- "microservice",
36
- "monolith",
37
- "nodejs",
38
- "generator",
39
- "scaffold",
40
- "docker",
41
- "pm2",
42
- "jwt",
43
- "authentication",
44
- "colored-cli"
45
- ],
46
- "license": "MIT",
47
- "author": "Aladetan Fortune Ifeloju (IfeCodes) <ifecodes01@gmail.com>",
48
- "dependencies": {
49
- "picocolors": "^1.1.1",
50
- "prompts": "^2.4.2"
51
- },
52
- "engines": {
53
- "node": ">=18.0.0"
54
- }
55
- }
1
+ {
2
+ "name": "@ifecodes/backend-template",
3
+ "version": "1.4.0",
4
+ "description": "Production-ready Express + TypeScript/JavaScript backend generator with optional features and microservice support",
5
+ "bin": {
6
+ "ifecodes-template": "bin/cli.js"
7
+ },
8
+ "type": "module",
9
+ "files": [
10
+ "bin",
11
+ "template"
12
+ ],
13
+ "scripts": {
14
+ "lint": "eslint .",
15
+ "format": "prettier --write ."
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/ALADETAN-IFE/backend-template.git"
20
+ },
21
+ "bugs": {
22
+ "url": "https://github.com/ALADETAN-IFE/backend-template/issues"
23
+ },
24
+ "homepage": "https://backend-template-generator.vercel.app",
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "keywords": [
29
+ "cli",
30
+ "template",
31
+ "boilerplate",
32
+ "typescript",
33
+ "javascript",
34
+ "backend",
35
+ "express",
36
+ "microservice",
37
+ "monolith",
38
+ "nodejs",
39
+ "generator",
40
+ "scaffold",
41
+ "docker",
42
+ "pm2",
43
+ "jwt",
44
+ "authentication",
45
+ "colored-cli"
46
+ ],
47
+ "license": "MIT",
48
+ "author": "Aladetan Fortune Ifeloju (IfeCodes) <fortuneifealadetan01@gmail.com>",
49
+ "dependencies": {
50
+ "picocolors": "^1.1.1",
51
+ "prompts": "^2.4.2"
52
+ },
53
+ "engines": {
54
+ "node": ">=18.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^20.1.0",
58
+ "@typescript-eslint/eslint-plugin": "^8.60.0",
59
+ "@typescript-eslint/parser": "^8.60.0",
60
+ "eslint": "^10.4.1",
61
+ "eslint-config-prettier": "^10.1.8",
62
+ "prettier": "^2.8.8"
63
+ }
64
+ }
@@ -1,13 +1,10 @@
1
- {
2
- "extends": [
3
- "eslint:recommended",
4
- "prettier"
5
- ],
6
- "env": {
7
- "node": true,
8
- "es2021": true
9
- },
10
- "rules": {
11
- "no-unused-vars": ["warn"]
12
- }
13
- }
1
+ {
2
+ "extends": ["eslint:recommended", "prettier"],
3
+ "env": {
4
+ "node": true,
5
+ "es2021": true
6
+ },
7
+ "rules": {
8
+ "no-unused-vars": ["warn"]
9
+ }
10
+ }
@@ -1,7 +1,7 @@
1
- {
2
- "semi": true,
3
- "singleQuote": true,
4
- "trailingComma": "es5",
5
- "printWidth": 80,
6
- "tabWidth": 2
7
- }
1
+ {
2
+ "semi": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "printWidth": 80,
6
+ "tabWidth": 2
7
+ }
@@ -1,31 +1,33 @@
1
- module.exports = [
2
- {
3
- ignores: ["node_modules/**", "dist/**"],
4
- },
5
- {
6
- files: ["src/**/*.js"],
7
- languageOptions: {
8
- ecmaVersion: 2021,
9
- sourceType: "module",
10
- globals: {
11
- console: "readonly",
12
- process: "readonly",
13
- Buffer: "readonly",
14
- __dirname: "readonly",
15
- __filename: "readonly",
16
- require: "readonly",
17
- module: "readonly",
18
- exports: "writable",
19
- global: "readonly",
20
- setTimeout: "readonly",
21
- setInterval: "readonly",
22
- clearTimeout: "readonly",
23
- clearInterval: "readonly",
24
- },
25
- },
26
- rules: {
27
- "no-unused-vars": "warn",
28
- "no-undef": "error",
29
- },
30
- },
31
- ];
1
+ const tsParser = require('@typescript-eslint/parser');
2
+ const tsPlugin = require('@typescript-eslint/eslint-plugin');
3
+
4
+ module.exports = [
5
+ // Files/paths to ignore (replaces .eslintignore usage in flat config)
6
+ {
7
+ ignores: ['node_modules/**', 'dist/**'],
8
+ },
9
+
10
+ // TypeScript rules for source files
11
+ {
12
+ files: ['src/**/*.{ts,tsx}'],
13
+ languageOptions: {
14
+ parser: tsParser,
15
+ parserOptions: {
16
+ project: './tsconfig.json',
17
+ tsconfigRootDir: __dirname,
18
+ ecmaVersion: 2020,
19
+ sourceType: 'module',
20
+ },
21
+ },
22
+ plugins: {
23
+ '@typescript-eslint': tsPlugin,
24
+ },
25
+ rules: {
26
+ // Disallow explicit `any`
27
+ '@typescript-eslint/no-explicit-any': 'error',
28
+
29
+ // You can add or tune more TypeScript rules here
30
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
31
+ },
32
+ },
33
+ ];
@@ -1,28 +1,29 @@
1
- {
2
- "version": "1.0.0",
3
- "description": "",
4
- "main": "index.js",
5
- "scripts": {
6
- "dev": "nodemon --exec node src/server.js",
7
- "start": "node src/server.js",
8
- "lint": "eslint \"src/**/*.js\"",
9
- "format": "prettier --write \"src/**/*.{js,json}\"",
10
- "check-format": "prettier --check \"src/**/*.{js,json}\"",
11
- "prepare": "husky"
12
- },
13
- "keywords": [],
14
- "author": "",
15
- "license": "ISC",
16
- "type": "commonjs",
17
- "dependencies": {
18
- "dotenv": "^16.3.1",
19
- "express": "^5.2.1"
20
- },
21
- "devDependencies": {
22
- "eslint": "^9.39.2",
23
- "eslint-config-prettier": "^10.1.8",
24
- "husky": "^9.1.7",
25
- "nodemon": "^3.0.2",
26
- "prettier": "^3.7.4"
27
- }
28
- }
1
+ {
2
+ "version": "1.0.0",
3
+ "description": "",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "dev": "nodemon --exec node src/server.js",
7
+ "start": "node src/server.js",
8
+ "lint": "eslint \"src/**/*.js\"",
9
+ "format": "prettier --write \"src/**/*.{js,json}\"",
10
+ "check-format": "prettier --check \"src/**/*.{js,json}\"",
11
+ "prepare": "husky"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC",
16
+ "type": "commonjs",
17
+ "dependencies": {
18
+ "dotenv": "^16.3.1",
19
+ "express": "^5.2.1",
20
+ "swagger-ui-express": "^5.0.1"
21
+ },
22
+ "devDependencies": {
23
+ "eslint": "^9.39.2",
24
+ "eslint-config-prettier": "^10.1.8",
25
+ "husky": "^9.1.7",
26
+ "nodemon": "^3.0.2",
27
+ "prettier": "^3.7.4"
28
+ }
29
+ }
@@ -1,18 +1,20 @@
1
- const express = require("express");
2
- const router = require("./routes");
3
- const { errorHandler } = require("./middlewares");
4
- /*__IMPORTS__*/
5
-
6
- const app = express();
7
-
8
- // Parse JSON request bodies
9
- app.use(express.json());
10
-
11
- /*__MIDDLEWARE__*/
12
-
13
- // Connect routes
14
- app.use(router);
15
-
16
- app.use(errorHandler);
17
-
18
- module.exports = app;
1
+ const express = require('express');
2
+ const router = require('./routes');
3
+ const { errorHandler, observabilityMiddleware } = require('./middlewares');
4
+ /*__IMPORTS__*/
5
+
6
+ const app = express();
7
+
8
+ // Parse JSON request bodies
9
+ app.use(express.json());
10
+
11
+ app.use(observabilityMiddleware);
12
+
13
+ /*__MIDDLEWARE__*/
14
+
15
+ // Connect routes
16
+ app.use(router);
17
+
18
+ app.use(errorHandler);
19
+
20
+ module.exports = app;
@@ -1,8 +1,8 @@
1
- // MongoDB connection will be added here when authentication is enabled
2
- const connectDB = async () => {
3
- // Database connection placeholder
4
- };
5
-
6
- module.exports = {
7
- connectDB,
8
- };
1
+ // MongoDB connection will be added here when authentication is enabled
2
+ const connectDB = async () => {
3
+ // Database connection placeholder
4
+ };
5
+
6
+ module.exports = {
7
+ connectDB,
8
+ };
@@ -1,14 +1,44 @@
1
- const dotenv = require("dotenv");
2
- dotenv.config();
3
-
4
- const ENV = {
5
- PORT: process.env.PORT,
6
- /*__ALLOWED_ORIGIN__*/
7
- NODE_ENV: process.env.NODE_ENV,
8
- /*__MONGO_URI__*/
9
- /*__JWT_SECRET__*/
10
- };
11
-
12
- module.exports = {
13
- ENV,
14
- };
1
+ const dotenv = require('dotenv');
2
+ dotenv.config();
3
+
4
+ const colors = {
5
+ reset: '\x1b[0m',
6
+ red: '\x1b[31m',
7
+ bold: '\x1b[1m',
8
+ };
9
+
10
+ function format(tag, color) {
11
+ return `${color}${colors.bold}[${tag}]${colors.reset}`;
12
+ }
13
+
14
+ const validateEnv = (env) => {
15
+ const missing = Object.entries(env)
16
+ .filter(([, value]) => value === undefined || value === '')
17
+ .map(([key]) => key);
18
+
19
+ if (missing.length > 0) {
20
+ console.error(
21
+ format('env', colors.red),
22
+ `Missing required environment variables: ${missing.join(', ')}`
23
+ );
24
+ console.error(
25
+ format('env', colors.red),
26
+ 'Please update your .env file and restart the server.'
27
+ );
28
+ process.exit(1);
29
+ }
30
+ };
31
+
32
+ const ENV = {
33
+ PORT: process.env.PORT,
34
+ /*__ALLOWED_ORIGIN__*/
35
+ NODE_ENV: process.env.NODE_ENV,
36
+ /*__MONGO_URI__*/
37
+ /*__JWT_SECRET__*/
38
+ };
39
+
40
+ validateEnv(ENV);
41
+
42
+ module.exports = {
43
+ ENV,
44
+ };
@@ -1,7 +1,7 @@
1
- const { connectDB } = require("./db");
2
- const { ENV } = require("./env");
3
-
4
- module.exports = {
5
- connectDB,
6
- ENV,
7
- };
1
+ const { connectDB } = require('./db');
2
+ const { ENV } = require('./env');
3
+
4
+ module.exports = {
5
+ connectDB,
6
+ ENV,
7
+ };
@@ -0,0 +1,5 @@
1
+ const { routeRegistry } = require('./route-registry');
2
+
3
+ module.exports = {
4
+ routeRegistry,
5
+ };
@@ -0,0 +1,63 @@
1
+ class RouteRegistry {
2
+ constructor() {
3
+ this.routes = [];
4
+ }
5
+
6
+ register(route) {
7
+ this.routes.push(route);
8
+ }
9
+
10
+ getRoutes() {
11
+ return this.routes;
12
+ }
13
+
14
+ generateOpenAPI(projectName, version = '1.0.0') {
15
+ const paths = {};
16
+ const tags = new Set();
17
+
18
+ // Collect all unique tags
19
+ this.routes.forEach((route) => {
20
+ route.docs.tags?.forEach((tag) => tags.add(tag));
21
+ });
22
+
23
+ // Build paths from routes
24
+ this.routes.forEach((route) => {
25
+ if (!paths[route.path]) {
26
+ paths[route.path] = {};
27
+ }
28
+
29
+ const pathItem = paths[route.path];
30
+ const method = route.method.toLowerCase();
31
+
32
+ pathItem[method] = {
33
+ tags: route.docs.tags || [],
34
+ summary: route.docs.summary,
35
+ description: route.docs.description,
36
+ parameters: route.docs.parameters || [],
37
+ requestBody: route.docs.requestBody,
38
+ responses: route.docs.responses,
39
+ };
40
+ });
41
+
42
+ return {
43
+ openapi: '3.0.3',
44
+ info: {
45
+ title: `${projectName} API`,
46
+ version,
47
+ description: 'Auto-generated from route schemas.',
48
+ },
49
+ servers: [{ url: 'http://localhost:4000' }],
50
+ tags: Array.from(tags).map((tag) => ({
51
+ name: tag,
52
+ description: `${tag} endpoints`,
53
+ })),
54
+ paths,
55
+ };
56
+ }
57
+ }
58
+
59
+ const routeRegistry = new RouteRegistry();
60
+
61
+ module.exports = {
62
+ routeRegistry,
63
+ };
@@ -1,19 +1,22 @@
1
- const { HttpError } = require("../utils");
2
-
3
- const errorHandler = (err, _, res, __) => {
4
- if (err instanceof HttpError) {
5
- return res.status(err.status).json({
6
- status: "error",
7
- message: err.message,
8
- });
9
- }
10
-
11
- return res.status(500).json({
12
- status: "error",
13
- message: "Internal Server Error",
14
- });
15
- };
16
-
17
- module.exports = {
18
- errorHandler,
19
- };
1
+ const { HttpError, logger } = require('../utils');
2
+
3
+ const errorHandler = (err, _, res, __) => {
4
+ if (err instanceof HttpError) {
5
+ logger.warn('ErrorHandler', `${err.status} ${err.message}`);
6
+ return res.status(err.status).json({
7
+ status: 'error',
8
+ message: err.message,
9
+ });
10
+ }
11
+
12
+ logger.error('ErrorHandler', 'Unhandled error', err);
13
+
14
+ return res.status(500).json({
15
+ status: 'error',
16
+ message: 'Internal Server Error',
17
+ });
18
+ };
19
+
20
+ module.exports = {
21
+ errorHandler,
22
+ };
@@ -1,11 +1,15 @@
1
- const methodNotAllowedHandler = require("./method-not-allowed.middleware");
2
- const { notFound } = require("./not-found.middleware");
3
- const { rootHandler } = require("./root.middleware");
4
- const { errorHandler } = require("./error-handler.middleware");
5
-
6
- module.exports = {
7
- methodNotAllowedHandler,
8
- notFound,
9
- rootHandler,
10
- errorHandler,
11
- };
1
+ const methodNotAllowedHandler = require('./method-not-allowed.middleware');
2
+ const { notFound } = require('./not-found.middleware');
3
+ const { rootHandler } = require('./root.middleware');
4
+ const { errorHandler } = require('./error-handler.middleware');
5
+ const { observabilityMiddleware } = require('./observability.middleware');
6
+ const { validateRequest } = require('./validation.middleware');
7
+
8
+ module.exports = {
9
+ methodNotAllowedHandler,
10
+ notFound,
11
+ rootHandler,
12
+ errorHandler,
13
+ observabilityMiddleware,
14
+ validateRequest,
15
+ };
@@ -1,13 +1,19 @@
1
- const methodNotAllowed = (allowedMethods) => (req, res, next) => {
2
- if (!allowedMethods.includes(req.method)) {
3
- res.set("Allow", allowedMethods.join(", "));
4
- return res.status(405).json({
5
- status: "error",
6
- message: `Method ${req.method} not allowed for ${req.originalUrl}`,
7
- allowed: allowedMethods,
8
- });
9
- }
10
- next();
11
- };
12
-
13
- module.exports = methodNotAllowed;
1
+ import { logger } from '@/utils';
2
+
3
+ const methodNotAllowed = (allowedMethods) => (req, res, next) => {
4
+ if (!allowedMethods.includes(req.method)) {
5
+ logger.warn(
6
+ 'MethodNotAllowed',
7
+ `${req.method} ${req.originalUrl} | allowed: ${allowedMethods.join(', ')}`
8
+ );
9
+ res.set('Allow', allowedMethods.join(', '));
10
+ return res.status(405).json({
11
+ status: 'error',
12
+ message: `Method ${req.method} not allowed for ${req.originalUrl}`,
13
+ allowed: allowedMethods,
14
+ });
15
+ }
16
+ next();
17
+ };
18
+
19
+ module.exports = methodNotAllowed;
@@ -1,10 +1,13 @@
1
- const notFound = (req, res) => {
2
- res.status(404).json({
3
- status: "error",
4
- message: `Route ${req.originalUrl} not found`,
5
- });
6
- };
7
-
8
- module.exports = {
9
- notFound,
10
- };
1
+ import { logger } from '@/utils';
2
+
3
+ const notFound = (req, res) => {
4
+ logger.warn('NotFound', `${req.method} ${req.originalUrl}`);
5
+ res.status(404).json({
6
+ status: 'error',
7
+ message: `Route ${req.originalUrl} not found`,
8
+ });
9
+ };
10
+
11
+ module.exports = {
12
+ notFound,
13
+ };
@@ -0,0 +1,24 @@
1
+ const { randomUUID } = require('crypto');
2
+ const { logger } = require('../utils');
3
+
4
+ const observabilityMiddleware = (req, res, next) => {
5
+ const requestId = req.get('X-Request-Id') || randomUUID();
6
+ const startedAt = Date.now();
7
+
8
+ res.locals.requestId = requestId;
9
+ res.setHeader('X-Request-Id', requestId);
10
+
11
+ res.on('finish', () => {
12
+ const duration = Date.now() - startedAt;
13
+ logger.info(
14
+ 'HTTP',
15
+ `${requestId} ${req.method} ${req.originalUrl} -> ${res.statusCode} (${duration}ms)`
16
+ );
17
+ });
18
+
19
+ next();
20
+ };
21
+
22
+ module.exports = {
23
+ observabilityMiddleware,
24
+ };