Skip to content

Commit e6ea674

Browse files
svozzadreamorosi
andauthored
feat(event-handler): add route specific middleware registration and execution (#4437)
Co-authored-by: Andrea Amorosi <[email protected]>
1 parent 0b6bbbb commit e6ea674

File tree

7 files changed

+813
-118
lines changed

7 files changed

+813
-118
lines changed

packages/event-handler/src/rest/BaseRouter.ts

Lines changed: 125 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ abstract class BaseRouter {
4242

4343
protected readonly routeRegistry: RouteHandlerRegistry;
4444
protected readonly errorHandlerRegistry: ErrorHandlerRegistry;
45-
protected readonly middlwares: Middleware[] = [];
45+
protected readonly middleware: Middleware[] = [];
4646

4747
/**
4848
* A logger instance to be used for logging debug, warning, and error messages.
@@ -170,7 +170,7 @@ abstract class BaseRouter {
170170
* ```
171171
*/
172172
public use(middleware: Middleware): void {
173-
this.middlwares.push(middleware);
173+
this.middleware.push(middleware);
174174
}
175175

176176
/**
@@ -223,7 +223,10 @@ abstract class BaseRouter {
223223
? route.handler.bind(options.scope)
224224
: route.handler;
225225

226-
const middleware = composeMiddleware([...this.middlwares]);
226+
const middleware = composeMiddleware([
227+
...this.middleware,
228+
...route.middleware,
229+
]);
227230

228231
const result = await middleware(
229232
route.params,
@@ -255,11 +258,11 @@ abstract class BaseRouter {
255258
}
256259

257260
public route(handler: RouteHandler, options: RouteOptions): void {
258-
const { method, path } = options;
261+
const { method, path, middleware = [] } = options;
259262
const methods = Array.isArray(method) ? method : [method];
260263

261264
for (const method of methods) {
262-
this.routeRegistry.register(new Route(method, path, handler));
265+
this.routeRegistry.register(new Route(method, path, handler, middleware));
263266
}
264267
}
265268

@@ -333,10 +336,26 @@ abstract class BaseRouter {
333336
#handleHttpMethod(
334337
method: HttpMethod,
335338
path: Path,
339+
middlewareOrHandler?: Middleware[] | RouteHandler,
336340
handler?: RouteHandler
337341
): MethodDecorator | undefined {
338-
if (handler && typeof handler === 'function') {
339-
this.route(handler, { method, path });
342+
if (Array.isArray(middlewareOrHandler)) {
343+
if (handler && typeof handler === 'function') {
344+
this.route(handler, { method, path, middleware: middlewareOrHandler });
345+
return;
346+
}
347+
return (_target, _propertyKey, descriptor: PropertyDescriptor) => {
348+
this.route(descriptor.value, {
349+
method,
350+
path,
351+
middleware: middlewareOrHandler,
352+
});
353+
return descriptor;
354+
};
355+
}
356+
357+
if (middlewareOrHandler && typeof middlewareOrHandler === 'function') {
358+
this.route(middlewareOrHandler, { method, path });
340359
return;
341360
}
342361

@@ -347,54 +366,142 @@ abstract class BaseRouter {
347366
}
348367

349368
public get(path: Path, handler: RouteHandler): void;
369+
public get(path: Path, middleware: Middleware[], handler: RouteHandler): void;
350370
public get(path: Path): MethodDecorator;
351-
public get(path: Path, handler?: RouteHandler): MethodDecorator | undefined {
352-
return this.#handleHttpMethod(HttpVerbs.GET, path, handler);
371+
public get(path: Path, middleware: Middleware[]): MethodDecorator;
372+
public get(
373+
path: Path,
374+
middlewareOrHandler?: Middleware[] | RouteHandler,
375+
handler?: RouteHandler
376+
): MethodDecorator | undefined {
377+
return this.#handleHttpMethod(
378+
HttpVerbs.GET,
379+
path,
380+
middlewareOrHandler,
381+
handler
382+
);
353383
}
354384

355385
public post(path: Path, handler: RouteHandler): void;
386+
public post(
387+
path: Path,
388+
middleware: Middleware[],
389+
handler: RouteHandler
390+
): void;
356391
public post(path: Path): MethodDecorator;
357-
public post(path: Path, handler?: RouteHandler): MethodDecorator | undefined {
358-
return this.#handleHttpMethod(HttpVerbs.POST, path, handler);
392+
public post(path: Path, middleware: Middleware[]): MethodDecorator;
393+
public post(
394+
path: Path,
395+
middlewareOrHandler?: Middleware[] | RouteHandler,
396+
handler?: RouteHandler
397+
): MethodDecorator | undefined {
398+
return this.#handleHttpMethod(
399+
HttpVerbs.POST,
400+
path,
401+
middlewareOrHandler,
402+
handler
403+
);
359404
}
360405

361406
public put(path: Path, handler: RouteHandler): void;
407+
public put(path: Path, middleware: Middleware[], handler: RouteHandler): void;
362408
public put(path: Path): MethodDecorator;
363-
public put(path: Path, handler?: RouteHandler): MethodDecorator | undefined {
364-
return this.#handleHttpMethod(HttpVerbs.PUT, path, handler);
409+
public put(path: Path, middleware: Middleware[]): MethodDecorator;
410+
public put(
411+
path: Path,
412+
middlewareOrHandler?: Middleware[] | RouteHandler,
413+
handler?: RouteHandler
414+
): MethodDecorator | undefined {
415+
return this.#handleHttpMethod(
416+
HttpVerbs.PUT,
417+
path,
418+
middlewareOrHandler,
419+
handler
420+
);
365421
}
366422

367423
public patch(path: Path, handler: RouteHandler): void;
424+
public patch(
425+
path: Path,
426+
middleware: Middleware[],
427+
handler: RouteHandler
428+
): void;
368429
public patch(path: Path): MethodDecorator;
430+
public patch(path: Path, middleware: Middleware[]): MethodDecorator;
369431
public patch(
370432
path: Path,
433+
middlewareOrHandler?: Middleware[] | RouteHandler,
371434
handler?: RouteHandler
372435
): MethodDecorator | undefined {
373-
return this.#handleHttpMethod(HttpVerbs.PATCH, path, handler);
436+
return this.#handleHttpMethod(
437+
HttpVerbs.PATCH,
438+
path,
439+
middlewareOrHandler,
440+
handler
441+
);
374442
}
375443

376444
public delete(path: Path, handler: RouteHandler): void;
445+
public delete(
446+
path: Path,
447+
middleware: Middleware[],
448+
handler: RouteHandler
449+
): void;
377450
public delete(path: Path): MethodDecorator;
451+
public delete(path: Path, middleware: Middleware[]): MethodDecorator;
378452
public delete(
379453
path: Path,
454+
middlewareOrHandler?: Middleware[] | RouteHandler,
380455
handler?: RouteHandler
381456
): MethodDecorator | undefined {
382-
return this.#handleHttpMethod(HttpVerbs.DELETE, path, handler);
457+
return this.#handleHttpMethod(
458+
HttpVerbs.DELETE,
459+
path,
460+
middlewareOrHandler,
461+
handler
462+
);
383463
}
384464

385465
public head(path: Path, handler: RouteHandler): void;
466+
public head(
467+
path: Path,
468+
middleware: Middleware[],
469+
handler: RouteHandler
470+
): void;
386471
public head(path: Path): MethodDecorator;
387-
public head(path: Path, handler?: RouteHandler): MethodDecorator | undefined {
388-
return this.#handleHttpMethod(HttpVerbs.HEAD, path, handler);
472+
public head(path: Path, middleware: Middleware[]): MethodDecorator;
473+
public head(
474+
path: Path,
475+
middlewareOrHandler?: Middleware[] | RouteHandler,
476+
handler?: RouteHandler
477+
): MethodDecorator | undefined {
478+
return this.#handleHttpMethod(
479+
HttpVerbs.HEAD,
480+
path,
481+
middlewareOrHandler,
482+
handler
483+
);
389484
}
390485

391486
public options(path: Path, handler: RouteHandler): void;
487+
public options(
488+
path: Path,
489+
middleware: Middleware[],
490+
handler: RouteHandler
491+
): void;
392492
public options(path: Path): MethodDecorator;
493+
public options(path: Path, middleware: Middleware[]): MethodDecorator;
393494
public options(
394495
path: Path,
496+
middlewareOrHandler?: Middleware[] | RouteHandler,
395497
handler?: RouteHandler
396498
): MethodDecorator | undefined {
397-
return this.#handleHttpMethod(HttpVerbs.OPTIONS, path, handler);
499+
return this.#handleHttpMethod(
500+
HttpVerbs.OPTIONS,
501+
path,
502+
middlewareOrHandler,
503+
handler
504+
);
398505
}
399506
}
400507

packages/event-handler/src/rest/Route.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
import type { HttpMethod, Path, RouteHandler } from '../types/rest.js';
1+
import type {
2+
HttpMethod,
3+
Middleware,
4+
Path,
5+
RouteHandler,
6+
} from '../types/rest.js';
27

38
class Route {
49
readonly id: string;
510
readonly method: string;
611
readonly path: Path;
712
readonly handler: RouteHandler;
13+
readonly middleware: Middleware[];
814

9-
constructor(method: HttpMethod, path: Path, handler: RouteHandler) {
15+
constructor(
16+
method: HttpMethod,
17+
path: Path,
18+
handler: RouteHandler,
19+
middleware: Middleware[] = []
20+
) {
1021
this.id = `${method}:${path}`;
1122
this.method = method;
1223
this.path = path;
1324
this.handler = handler;
25+
this.middleware = middleware;
1426
}
1527
}
1628

packages/event-handler/src/rest/RouteHandlerRegistry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ class RouteHandlerRegistry {
160160
handler: staticRoute.handler,
161161
rawParams: {},
162162
params: {},
163+
middleware: staticRoute.middleware,
163164
};
164165
}
165166

@@ -182,6 +183,7 @@ class RouteHandlerRegistry {
182183
handler: route.handler,
183184
params: processedParams,
184185
rawParams: params,
186+
middleware: route.middleware,
185187
};
186188
}
187189
}

packages/event-handler/src/rest/utils.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ export const isAPIGatewayProxyResult = (
117117
* follows the onion model where middleware executes in order before `next()` and in
118118
* reverse order after `next()`.
119119
*
120-
* @param middlewares - Array of middleware functions to compose
121-
* @returns A single middleware function that executes all provided middlewares in sequence
120+
* @param middleware - Array of middleware functions to compose
121+
* @returns A single middleware function that executes all provided middleware in sequence
122122
*
123123
* @example
124124
* ```typescript
@@ -143,7 +143,7 @@ export const isAPIGatewayProxyResult = (
143143
* // -> middleware1 end
144144
* ```
145145
*/
146-
export const composeMiddleware = (middlewares: Middleware[]): Middleware => {
146+
export const composeMiddleware = (middleware: Middleware[]): Middleware => {
147147
return async (
148148
params: Record<string, string>,
149149
options: RequestOptions,
@@ -156,16 +156,16 @@ export const composeMiddleware = (middlewares: Middleware[]): Middleware => {
156156
if (i <= index) throw new Error('next() called multiple times');
157157
index = i;
158158

159-
if (i === middlewares.length) {
159+
if (i === middleware.length) {
160160
const nextResult = await next();
161161
if (nextResult !== undefined) {
162162
result = nextResult;
163163
}
164164
return;
165165
}
166166

167-
const middleware = middlewares[i];
168-
const middlewareResult = await middleware(params, options, () =>
167+
const middlewareFn = middleware[i];
168+
const middlewareResult = await middlewareFn(params, options, () =>
169169
dispatch(i + 1)
170170
);
171171

packages/event-handler/src/types/rest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@ type RouteHandlerOptions = {
7070
handler: RouteHandler;
7171
params: Record<string, string>;
7272
rawParams: Record<string, string>;
73+
middleware: Middleware[];
7374
};
7475

7576
type RouteOptions = {
7677
method: HttpMethod | HttpMethod[];
7778
path: Path;
79+
middleware?: Middleware[];
7880
};
7981

8082
type NextFunction = () => Promise<HandlerResponse | void>;

0 commit comments

Comments
 (0)