diff --git a/packages/event-handler/src/rest/Router.ts b/packages/event-handler/src/rest/Router.ts index d54cec4a02..0dfeeecadf 100644 --- a/packages/event-handler/src/rest/Router.ts +++ b/packages/event-handler/src/rest/Router.ts @@ -56,6 +56,10 @@ class Router { * Whether the router is running in development mode. */ protected readonly isDev: boolean = false; + /** + * The base prefix to be used for all routes registered using this Router. + */ + protected readonly prefix?: Path; public constructor(options?: RestRouterOptions) { this.context = {}; @@ -73,6 +77,7 @@ class Router { logger: this.logger, }); this.isDev = isDevMode(); + this.prefix = options?.prefix; } /** @@ -238,9 +243,9 @@ class Router { ); } else { const handler = - options?.scope != null - ? route.handler.bind(options.scope) - : route.handler; + options?.scope == null + ? route.handler + : route.handler.bind(options.scope); const handlerResult = await handler(params, reqCtx); reqCtx.res = handlerResultToWebResponse( @@ -281,9 +286,15 @@ class Router { public route(handler: RouteHandler, options: RestRouteOptions): void { const { method, path, middleware = [] } = options; const methods = Array.isArray(method) ? method : [method]; + let resolvedPath = path; + if (this.prefix) { + resolvedPath = path === '/' ? this.prefix : `${this.prefix}${path}`; + } for (const method of methods) { - this.routeRegistry.register(new Route(method, path, handler, middleware)); + this.routeRegistry.register( + new Route(method, resolvedPath, handler, middleware) + ); } } diff --git a/packages/event-handler/src/types/rest.ts b/packages/event-handler/src/types/rest.ts index e4924d1d2c..0b660330bd 100644 --- a/packages/event-handler/src/types/rest.ts +++ b/packages/event-handler/src/types/rest.ts @@ -43,6 +43,10 @@ type RestRouterOptions = { * When no logger is provided, we'll only log warnings and errors using the global `console` object. */ logger?: GenericLogger; + /** + * The base prefix to be used for all routes registered using this Router. + */ + prefix?: Path; }; interface CompiledRoute { diff --git a/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts b/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts index 0fe0d4182d..b983b4ed2b 100644 --- a/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts +++ b/packages/event-handler/tests/unit/rest/Router/basic-routing.test.ts @@ -117,4 +117,31 @@ describe('Class: Router - Basic Routing', () => { InternalServerError ); }); + + it('routes to the prefixed path when having a shared prefix defined', async () => { + // Prepare + const app = new Router({ + prefix: '/todos', + }); + app.post('/', async () => { + return { actualPath: '/todos' }; + }); + app.get('/:todoId', async ({ todoId }) => { + return { actualPath: `/todos/${todoId}` }; + }); + + // Act + const createResult = await app.resolve( + createTestEvent('/todos', 'POST'), + context + ); + const getResult = await app.resolve( + createTestEvent('/todos/1', 'GET'), + context + ); + + // Assess + expect(JSON.parse(createResult.body).actualPath).toBe('/todos'); + expect(JSON.parse(getResult.body).actualPath).toBe('/todos/1'); + }); });