-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[RFC] Introduce express middleware extension point #2434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ee92c41 to
2e58dc3
Compare
2dac020 to
b71eedb
Compare
2e58dc3 to
8dc57b3
Compare
cc5ace0 to
9874848
Compare
|
I am not sure if it's a good idea to re-introduce middleware phases in LB4. From the comments I seen in the past, many LB3 users are not "getting it", they don't understand why should they use middleware phases instead of vanilla express style of registration. Do we still want to purpose Sequence as the tool for orchestrating request handling pipeline? I think we should focus more on Sequence and treat compatibility with Express middleware as a short-term workaround only. As I see it, there are principally four middleware phases:
In LoopBack 3, the primary motivation for introducing middleware phases was to ensure correct order of invocation of these different middleware types. IIRC, the concept of finer-grained phases for request pre-processing was a kind of a nice side effect. In LoopBack 4, there is already infrastructure in place to enforce these four phases. What we are missing is API that would allow app developers to register their own request pre-processing middleware. As I see understand the proposal in your PR, it is splitting the request pre-processing phase into multiple finer-grained sub-phases. While I see how that can be useful, I am not convinced that the benefits justify the additional complexity (both for our users and the framework codebase). |
|
In fact, I would like to refactor this PR into a separate module such as
It's my intent to create an For LoopBack itself, I personally don't think the sequence of actions is good enough to take over the Express middleware pipeline and we should keep the door open for developers to use existing middleware. |
9874848 to
9200f41
Compare
8dc57b3 to
2366aa4
Compare
eb4c08e to
2daf3f1
Compare
|
I refactored the code into |
|
I would like to port |
38b11a4 to
e9cc630
Compare
bajtos
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This patch is adding a new package. Have you followed all steps in our
checklist? DEVELOPING.md#adding-a-new-package
How do you envision error handling for errors reported from Express middleware? At the moment, LB4 pretty much guarantees that all errors will be routed to reject sequence action. IIUC, your proposal is changing that - some errors are handled by reject, errors from middleware is handled by the non-customizable final error handler in the RestServer.
On a similar topic: at the moment, it's easy to observe when request handling started and finished, because everything happens inside sequence. For example, to log duration of a request (including failed requests!), one can write similar sequence:
export class MySequence implements SequenceHandler {
// constructor with @inject decorators
async handle(context: RequestContext) {
const {request, response} = context;
const start = process.hrtime();
try {
const route = this.findRoute(request);
const args = await this.parseParams(request, route);
const result = await this.invoke(route, args);
this.send(response, result);
} catch (error) {
this.reject(context, error);
}
const delta = process.hrtime(start);
console.log(`Handled "${request.method} ${request.url}" in ${delta[0]} seconds.`);
}
}This won't be possible with your new extension in place.
IMO, we should move middleware invocation into a new sequence action.
export class MySequence implements SequenceHandler {
// constructor with @inject decorators
async handle(context: RequestContext) {
const {request, response} = context;
const start = process.hrtime();
try {
this.executeMiddleware();
const route = this.findRoute(request);
const args = await this.parseParams(request, route);
const result = await this.invoke(route, args);
this.send(response, result);
} catch (error) {
this.reject(context, error);
}
const delta = process.hrtime(start);
console.log(`Handled "${request.method} ${request.url}" in ${delta[0]} seconds.`);
}
}Anyhow. I think the most important task now is to find a different way how to extend @loopback/rest with support for custom middleware, an extension point that will allow LB4 developers to experiment with different approaches for middleware registration.
|
|
||
| ```ts | ||
| const expressCtx = new ExpressContext(); | ||
| expressCtx.middleware(cors(), {phase: 'cors'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you expect users to call middleware with a single argument only? If not, then I think the following API may be more ergonomic:
expressCtx.middleware({
handler: cors(),
phase: 'cors',
// path, etc.
});
packages/rest/src/rest.server.ts
Outdated
| this._requestHandler = this._expressApp; | ||
|
|
||
| const expressCtx = new ExpressContext(this); | ||
| expressCtx.setMiddlewareRegistryOptions({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not comfortable forcing Express context on all LoopBack4 users.
Can you find a way how to make this extension an optional addition please?
| this._expressApp.get(p, (req, res) => | ||
| this._serveOpenApiSpec(req, res, mapping[p]), | ||
| expressCtx.middleware( | ||
| (req, res) => this._serveOpenApiSpec(req, res, mapping[p]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that by now, our REST layer should be providing enough features to allow us to implement _serverOpenApiSpec routes as regular LB4 routes with x-visibility: undocumented. Instead of relying on your new extension, can we rework this part to use existing LB4 means?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, it looks like it was an intentional decision to implement these routes at REST API layer, bypassing the sequence.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh oh, this is even more complex than I thought. It turns out that OpenAPI endpoints like /openapi.json do not honor basePath configuration set at server level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can see the RouteEntry-based implementation of /openapi.json here: 2b5be22
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uh oh, this is even more complex than I thought. It turns out that OpenAPI endpoints like
/openapi.jsondo not honorbasePathconfiguration set at server level.
This problem is already tracked here: #2329
| this._expressApp.get(explorerPaths, (req, res, next) => | ||
| this._redirectToSwaggerUI(req, res, next), | ||
| expressCtx.middleware( | ||
| (req, res, next) => this._redirectToSwaggerUI(req, res, next), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2beb2a0 to
cb12520
Compare
cb12520 to
69bec51
Compare
89c03be to
ab241f6
Compare
ab241f6 to
680838e
Compare
680838e to
969be21
Compare
969be21 to
6587a8a
Compare
6587a8a to
537f21b
Compare
6751ef0 to
81626cc
Compare
81626cc to
fe54778
Compare
0ce50d3 to
7360f76
Compare
Add @loopback/express-middleware as a new package
7360f76 to
36233c8
Compare
|
Close to favor #5118 |
This PR introduces an extension point for express middleware so that they can be added and sorted by phase to force a hierarchical router.
Doc: https://github.com/strongloop/loopback-next/blob/express-middleware/packages/express-middleware/README.md
See also #1293
Checklist
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated