Express: need orginal path to generate api docs

Created on 24 Oct 2019  ·  16Comments  ·  Source: expressjs/express

I want to get all the router routing information by traversing app._route.stack and automatically generate the swagger json document in combination with joi. However, Layer Contructor directly parses the path into regexp, and I can't get the original path.

question

Most helpful comment

@GuskiS, so that I am clear what you mean, you want to log the parameterized route (ex: /user/:id) along with your application logs so that you can trace logs together for which route triggered that code path?

If so, then I can see that this might be a more "general purpose" feature than specifically the "generate docs" use case. Depending on how you structure your app/routes you can get some unexpected results from req.route, but still seems like a reasonable ask.

All of this has made me take a look at what I did in the open api module, and it would actually simplify some code I wrote there if we did add some reference to the original "route" before path-to-regexp parses it.

That all said, I think if we want to move forward with this idea we should move the conversation over to the router repo.

All 16 comments

@MichelLiu does this module work for you? https://github.com/wesleytodd/express-openapi/

Oh I posted in the closed PR with the same link! :)

@dougwilson, we never really landed on the final agreement, but if you are not opposed I could move that over to the express org and get it published under the scope. This topic currently has 3 or 4 related open issues which I could follow up on if we are in agreement that my solution to this works.

@dougwilson ,
We have used express online and cannot be replaced directly with express-openapi. I temporarily wrote this to reverse the regexp to the original path.

function getFullPath(layer) {
        let routeName = "";
        if (layer.regexp) {
            routeName = layer.regexp.source.replace("^\\","").replace("\\/?(?=\\/|$)","");
            if (layer.keys && layer.keys.length > 0) {
                layer.keys.forEach(function (key) {
                    routeName = routeName.replace("(?:([^\\/]+?))",":" + key.name);
                });
            }
        }
        return routeName.split("\\").join("");
}

We have used express online and cannot be replaced directly with express-openapi

I'm not sure what this means. Can you ellaborate? Perhaps you can help us understand what you are trying to achieve in some way? For example, you can maybe provide a demo of what you would do with the given path?

eg.
//router.js

router.post('/blog', joiSwagger.joiValidator({
    summary:"blog",
    description: "博客",
    validators: {
        body: joi.object().keys({
            comboName: joi.string().when('role', { is: 'mainaccount', then: joi.string().required() }).when('role', { is: 'subaccount', then: joi.string().allow('') })
        })
    },
    responseExamples: [
        {
            code: 200,
            description: "normal response",
            schema: joi.object().keys({
                "error_code": joi.number().integer().default(0).description("错误码"),
                "message": joi.string().default("success").description("错误字符串"),
                "success": joi.boolean().default(true),
                "data": {}
            })
        }
    ]
}) ,function (req, res) {
    console.log('/api/blog');

    res.json(req.body);
});

//app.js
app.use("/api/:cb", require("./router"));

//this function needs to concat '/api/:cb' with '/blog' in order to generate swagger document
convertToSwaggerDoc(app._router.stack)

// function definition

stack.forEach(function (layer) {
  layer.handle.stack.forEach(function (subLayer) {
                    if (subLayer.route && subLayer.route.stack) {
                        subLayer.route.stack.forEach(function (middlewareFunction) {
                            if (middlewareFunction.name === "validateJoi") {
                                const options = middlewareFunction.routedef || middlewareFunction.handle.routedef;
                                const path = getFullPath(layer) + subLayer.route.path;
                                const method = layer.methods ? layer.methods[layer.methods.length-1].toLowerCase() : middlewareFunction.method;
                                thisPointer.addRouteDefinition(method, path);
                            }
                        });
                    }
                })
}

So what you are trying to do is exactly what the module I linked to does. Why is it not usable, specifically?

We need a joiSwagger middleware that can do both joi parameter validation and convert express or eggjs app.stack to swagger documentation. We have already launched the middleware and proposed this issue just to reduce the operation of a path conversion. If I use express-openapi, I need to change a lot of code, which is out of date.

So the issue with your proposed solution would not really work any better; users can directly use regular expressions to define a path, so you're always going to have to reverse a regular expression back into the path. The reason we only keep the regular expression is because of the multiple ways in which a user can define an Express route, that is the least-common-denominator. If we add your proposed property to Express, what is your method to support regular expression paths?

As for the regular expression paths, we are directly converted to a form of toString. The normal path we show is normal.

@MichelLiu, to your specific issue: Express cannot add features just because one use case where you are combining other modules is difficult. I am 👎 on this PR because of that.

That said, I think this is a very common requirement, this issue is an example of the reason I think we should have a solution for this maintained directly for Express. Lack of it has led more than just @MichelLiu into a position like this (my current team and others I have talked with on the topic included).

I have similar case - I need to group my request logs based on path, but since express transforms path and doesn't provide original path it is not possible.

@GuskiS, so that I am clear what you mean, you want to log the parameterized route (ex: /user/:id) along with your application logs so that you can trace logs together for which route triggered that code path?

If so, then I can see that this might be a more "general purpose" feature than specifically the "generate docs" use case. Depending on how you structure your app/routes you can get some unexpected results from req.route, but still seems like a reasonable ask.

All of this has made me take a look at what I did in the open api module, and it would actually simplify some code I wrote there if we did add some reference to the original "route" before path-to-regexp parses it.

That all said, I think if we want to move forward with this idea we should move the conversation over to the router repo.

@wesleytodd Yes, that is exactly what I want to do.

Ok, can one of you open a new issue detailing this request well so that we can have a better discussion on the router repo? I will close this one, but feel free to link back to it from the new issue.

I would also like to ask if we can better frame it around the underlying feature request and not just specifically justifying storing the original value. The reason is because I am very hesitant about adding that, as it seems like the general intention is then to build middleware and such around assuming the value there is just a single string without also properly handing regular expressions and arrays.

Was this page helpful?
0 / 5 - 0 ratings