I managed to implement as sample solution.
Here is the code demo: stakc-blitz modified
Sample description of the approach
I have not tested it but I wanted to show the approach.
We need a controller router builder to do that. And this controller builder will need to "stack" the types additions to the Request object of all middleware.
A sample
class ControllerBuilder<RequestType> {
addMiddleWare(middleWare): ControllerBuilder<RequestType & middlewareTypeAdditions> {
// implementation
}
}
In order to extract the middleware type - I need to have it stated someware.
That is why I introduced a decorated middleware.
Here is the abstraction over the Decorate middleware:
abstract class DecoratedMiddleware<MiddlewareReqTypeAdditions> {
///
}
Now in the ControllerBuilder we can "extract the type" of each middleware and "stack" them by returning new instance with unin type: ReqeustType so far united with the addition the new middleware will add
class ControllerBuilder<RequestType> {
addMiddleWare(middleWare: DecoratedMiddleware<MiddlewareReqTypeAdditions>): ControllerBuilder<RequestType & MiddlewareReqTypeAdditions> {
// implementation
return new ControllerBuilder<>
}
}
Here is a sample middleware implementation of. We only need to state the additional properties of the request, which the builder will set.
The process function has to return a Promise of those props, ensuring that all is set according to the middleware type contract.
type AuthRequestAddtion = {
role: string;
id: number | string;
hotelId: number;
};
class AuthMiddleware extends DecoratedMiddleware<AuthRequestAddtion> {
protected process: MuddlewareFunc<AuthRequestAddtion> = (req, res) => {
return Promise.resolve({
id: 1,
role: 'GUEST',
hotelId: 3,
});
};
}
And finally a sample usage:
ControllerBuilder.get(router(), '/with-weather')
.addMiddleware(authMiddleware)
.addMiddleware(multipartMiddleware)
.addMiddleware(weatherMiddleware)
.handle(async (req, res) => {
//now we have types for all the middlewares
const hotelId = req.hotelId;
const files = req.files;
const temp = req.weather.temperature;
res.status(200).json({ hotelId, files, temp });
});
The builder is not 100% complete, my intention was to show the approach.
I would probably modify it so that a set of middlwares can be used.
Note that it behaves as Builder until handle is called. So it is immutable and can be chained and result reused
Something like this:
const authUserWithWeather = ControllerBuilder.create()
.addMiddleware(authMiddleware)
.addMiddleware(weatherMiddleware);
authUserWithWeather.get("/").handle(() => {});
authUserWithWeather
.addMiddleware(multipartMiddleware)
.get("/something")
.handle(() => {})
Link to the demo again: stakc-blitz modified