deco-express 0.1.5 → 0.2.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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Express, RequestHandler } from 'express';
1
+ import { RequestHandler, Express } from 'express';
2
2
  import Joi from 'joi';
3
3
 
4
4
  /**
@@ -34,19 +34,24 @@ declare function Route(method: keyof Express, path?: string, ...middleware: Requ
34
34
  * and is prepended to every route path registered by {@link Route} decorators
35
35
  * on the class's methods.
36
36
  *
37
+ * Controller-level middleware is stored under the `"controllerMiddleware"` key
38
+ * and is prepended to every route handler in the controller.
39
+ *
37
40
  * @param baseRoute - The base path prefix for all routes in the controller
38
41
  * (e.g. `"/users"`). Defaults to `""` (no prefix).
42
+ * @param middleware - Zero or more Express middleware functions that are
43
+ * executed **before** every route handler in the controller.
39
44
  *
40
45
  * @example
41
46
  * ```ts
42
- * \@Controller('/users')
47
+ * \@Controller('/users', authMiddleware)
43
48
  * class UserController {
44
49
  * \@Route('get', '/:id')
45
50
  * getUser(req: Request, res: Response) { ... }
46
51
  * }
47
52
  * ```
48
53
  */
49
- declare function Controller(baseRoute?: string): (target: object) => void;
54
+ declare function Controller(baseRoute?: string, ...middleware: RequestHandler[]): (target: object) => void;
50
55
 
51
56
  /**
52
57
  * Method decorator that validates `req.body` against a Joi schema before the
@@ -91,8 +96,10 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
91
96
  * For each controller the function:
92
97
  * 1. Instantiates the class with `new`.
93
98
  * 2. Reads the `"baseRoute"` metadata set by {@link Controller}.
94
- * 3. Reads the `"routeHandlers"` metadata set by {@link Route}.
95
- * 4. Mounts each handler at the resolved full path on the Express app.
99
+ * 3. Reads the `"controllerMiddleware"` metadata set by {@link Controller}.
100
+ * 4. Reads the `"routeHandlers"` metadata set by {@link Route}.
101
+ * 5. Mounts each handler at the resolved full path on the Express app,
102
+ * with controller-level middleware prepended to each route's handlers.
96
103
  *
97
104
  * The full path is built as:
98
105
  * ```
@@ -105,6 +112,8 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
105
112
  * @param version - API version number. When `addApi` is `true` and `version`
106
113
  * is non-zero, a `/v{version}` segment is inserted after `/api`.
107
114
  * Defaults to `1`.
115
+ * @param log - When `true`, prints each registered route to the console.
116
+ * Defaults to `false`.
108
117
  *
109
118
  * @example
110
119
  * ```ts
@@ -113,11 +122,11 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
113
122
  * import { UserController } from './controllers/user';
114
123
  *
115
124
  * const app = express();
116
- * defineRoutes([UserController], app);
117
- * // Routes are now available at /api/v1/...
125
+ * defineRoutes([UserController], app, true, 1, true);
126
+ * // [deco-express] GET /api/v1/users/:id
118
127
  * ```
119
128
  */
120
- declare function defineRoutes(controllers: Constructor[], application: Express, addApi?: boolean, version?: number): void;
129
+ declare function defineRoutes(controllers: Constructor[], application: Express, addApi?: boolean, version?: number, log?: boolean): void;
121
130
 
122
131
  /**
123
132
  * A nested map that stores route handlers organised by HTTP method and path.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Express, RequestHandler } from 'express';
1
+ import { RequestHandler, Express } from 'express';
2
2
  import Joi from 'joi';
3
3
 
4
4
  /**
@@ -34,19 +34,24 @@ declare function Route(method: keyof Express, path?: string, ...middleware: Requ
34
34
  * and is prepended to every route path registered by {@link Route} decorators
35
35
  * on the class's methods.
36
36
  *
37
+ * Controller-level middleware is stored under the `"controllerMiddleware"` key
38
+ * and is prepended to every route handler in the controller.
39
+ *
37
40
  * @param baseRoute - The base path prefix for all routes in the controller
38
41
  * (e.g. `"/users"`). Defaults to `""` (no prefix).
42
+ * @param middleware - Zero or more Express middleware functions that are
43
+ * executed **before** every route handler in the controller.
39
44
  *
40
45
  * @example
41
46
  * ```ts
42
- * \@Controller('/users')
47
+ * \@Controller('/users', authMiddleware)
43
48
  * class UserController {
44
49
  * \@Route('get', '/:id')
45
50
  * getUser(req: Request, res: Response) { ... }
46
51
  * }
47
52
  * ```
48
53
  */
49
- declare function Controller(baseRoute?: string): (target: object) => void;
54
+ declare function Controller(baseRoute?: string, ...middleware: RequestHandler[]): (target: object) => void;
50
55
 
51
56
  /**
52
57
  * Method decorator that validates `req.body` against a Joi schema before the
@@ -91,8 +96,10 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
91
96
  * For each controller the function:
92
97
  * 1. Instantiates the class with `new`.
93
98
  * 2. Reads the `"baseRoute"` metadata set by {@link Controller}.
94
- * 3. Reads the `"routeHandlers"` metadata set by {@link Route}.
95
- * 4. Mounts each handler at the resolved full path on the Express app.
99
+ * 3. Reads the `"controllerMiddleware"` metadata set by {@link Controller}.
100
+ * 4. Reads the `"routeHandlers"` metadata set by {@link Route}.
101
+ * 5. Mounts each handler at the resolved full path on the Express app,
102
+ * with controller-level middleware prepended to each route's handlers.
96
103
  *
97
104
  * The full path is built as:
98
105
  * ```
@@ -105,6 +112,8 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
105
112
  * @param version - API version number. When `addApi` is `true` and `version`
106
113
  * is non-zero, a `/v{version}` segment is inserted after `/api`.
107
114
  * Defaults to `1`.
115
+ * @param log - When `true`, prints each registered route to the console.
116
+ * Defaults to `false`.
108
117
  *
109
118
  * @example
110
119
  * ```ts
@@ -113,11 +122,11 @@ type Constructor<T = object> = new (...args: unknown[]) => T;
113
122
  * import { UserController } from './controllers/user';
114
123
  *
115
124
  * const app = express();
116
- * defineRoutes([UserController], app);
117
- * // Routes are now available at /api/v1/...
125
+ * defineRoutes([UserController], app, true, 1, true);
126
+ * // [deco-express] GET /api/v1/users/:id
118
127
  * ```
119
128
  */
120
- declare function defineRoutes(controllers: Constructor[], application: Express, addApi?: boolean, version?: number): void;
129
+ declare function defineRoutes(controllers: Constructor[], application: Express, addApi?: boolean, version?: number, log?: boolean): void;
121
130
 
122
131
  /**
123
132
  * A nested map that stores route handlers organised by HTTP method and path.
package/dist/index.js CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
7
  var __export = (target, all) => {
@@ -18,14 +16,6 @@ var __copyProps = (to, from, except, desc) => {
18
16
  }
19
17
  return to;
20
18
  };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
- // If the importer is in node compatibility mode or this is not an ESM
23
- // file that has been converted to a CommonJS file using a Babel-
24
- // compatible transform (i.e. "__esModule" has not been set), then set
25
- // "default" to the CommonJS "module.exports" for node compatibility.
26
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
- mod
28
- ));
29
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
20
 
31
21
  // src/index.ts
@@ -51,15 +41,15 @@ function Route(method, path = "", ...middleware) {
51
41
  };
52
42
  }
53
43
  __name(Route, "Route");
54
- function Controller(baseRoute = "") {
44
+ function Controller(baseRoute = "", ...middleware) {
55
45
  return (target) => {
56
46
  Reflect.defineMetadata("baseRoute", baseRoute, target);
47
+ Reflect.defineMetadata("controllerMiddleware", middleware, target);
57
48
  };
58
49
  }
59
50
  __name(Controller, "Controller");
60
51
 
61
52
  // src/decorators/validation.ts
62
- var import_joi = __toESM(require("joi"));
63
53
  function Validate(schema) {
64
54
  return function(_target, _propertyKey, descriptor) {
65
55
  const originalMethod = descriptor.value;
@@ -67,16 +57,7 @@ function Validate(schema) {
67
57
  try {
68
58
  await schema.validateAsync(req.body);
69
59
  } catch (error) {
70
- if (error instanceof import_joi.default.ValidationError) {
71
- return res.status(422).json({
72
- message: "Validation failed",
73
- details: error.details.map((d) => ({
74
- field: d.path.join("."),
75
- message: d.message
76
- }))
77
- });
78
- }
79
- return next(error);
60
+ return res.status(422).json(error);
80
61
  }
81
62
  return originalMethod.call(this, req, res, next);
82
63
  };
@@ -86,7 +67,7 @@ function Validate(schema) {
86
67
  __name(Validate, "Validate");
87
68
 
88
69
  // src/functions/routes.ts
89
- function defineRoutes(controllers, application, addApi = true, version = 1) {
70
+ function defineRoutes(controllers, application, addApi = true, version = 1, log = false) {
90
71
  for (const ControllerClass of controllers) {
91
72
  const controller = new ControllerClass();
92
73
  const routeHandlers = Reflect.getMetadata(
@@ -94,12 +75,14 @@ function defineRoutes(controllers, application, addApi = true, version = 1) {
94
75
  controller
95
76
  );
96
77
  const controllerPath = Reflect.getMetadata("baseRoute", controller.constructor) ?? "";
78
+ const controllerMiddleware = Reflect.getMetadata("controllerMiddleware", controller.constructor) ?? [];
97
79
  if (!routeHandlers) continue;
98
80
  for (const [method, routes] of routeHandlers) {
99
81
  for (const [routePath, handlers] of routes) {
100
82
  const versionSegment = addApi && version ? `/v${version}` : "";
101
83
  const fullPath = `${addApi ? "/api" : ""}${versionSegment}${controllerPath}${routePath}`;
102
- application[method](fullPath, handlers);
84
+ application[method](fullPath, [...controllerMiddleware, ...handlers]);
85
+ if (log) console.log(`[deco-express] ${method.toUpperCase()} ${fullPath}`);
103
86
  }
104
87
  }
105
88
  }
package/dist/index.mjs CHANGED
@@ -16,15 +16,15 @@ function Route(method, path = "", ...middleware) {
16
16
  };
17
17
  }
18
18
  __name(Route, "Route");
19
- function Controller(baseRoute = "") {
19
+ function Controller(baseRoute = "", ...middleware) {
20
20
  return (target) => {
21
21
  Reflect.defineMetadata("baseRoute", baseRoute, target);
22
+ Reflect.defineMetadata("controllerMiddleware", middleware, target);
22
23
  };
23
24
  }
24
25
  __name(Controller, "Controller");
25
26
 
26
27
  // src/decorators/validation.ts
27
- import Joi from "joi";
28
28
  function Validate(schema) {
29
29
  return function(_target, _propertyKey, descriptor) {
30
30
  const originalMethod = descriptor.value;
@@ -32,16 +32,7 @@ function Validate(schema) {
32
32
  try {
33
33
  await schema.validateAsync(req.body);
34
34
  } catch (error) {
35
- if (error instanceof Joi.ValidationError) {
36
- return res.status(422).json({
37
- message: "Validation failed",
38
- details: error.details.map((d) => ({
39
- field: d.path.join("."),
40
- message: d.message
41
- }))
42
- });
43
- }
44
- return next(error);
35
+ return res.status(422).json(error);
45
36
  }
46
37
  return originalMethod.call(this, req, res, next);
47
38
  };
@@ -51,7 +42,7 @@ function Validate(schema) {
51
42
  __name(Validate, "Validate");
52
43
 
53
44
  // src/functions/routes.ts
54
- function defineRoutes(controllers, application, addApi = true, version = 1) {
45
+ function defineRoutes(controllers, application, addApi = true, version = 1, log = false) {
55
46
  for (const ControllerClass of controllers) {
56
47
  const controller = new ControllerClass();
57
48
  const routeHandlers = Reflect.getMetadata(
@@ -59,12 +50,14 @@ function defineRoutes(controllers, application, addApi = true, version = 1) {
59
50
  controller
60
51
  );
61
52
  const controllerPath = Reflect.getMetadata("baseRoute", controller.constructor) ?? "";
53
+ const controllerMiddleware = Reflect.getMetadata("controllerMiddleware", controller.constructor) ?? [];
62
54
  if (!routeHandlers) continue;
63
55
  for (const [method, routes] of routeHandlers) {
64
56
  for (const [routePath, handlers] of routes) {
65
57
  const versionSegment = addApi && version ? `/v${version}` : "";
66
58
  const fullPath = `${addApi ? "/api" : ""}${versionSegment}${controllerPath}${routePath}`;
67
- application[method](fullPath, handlers);
59
+ application[method](fullPath, [...controllerMiddleware, ...handlers]);
60
+ if (log) console.log(`[deco-express] ${method.toUpperCase()} ${fullPath}`);
68
61
  }
69
62
  }
70
63
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deco-express",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "author": "ndrolp",
5
5
  "description": "Decorator-based utilities for building Express.js REST APIs in TypeScript",
6
6
  "main": "./dist/index.js",