scottie1984 / swagger-ui-express

Adds middleware to your express app to serve the Swagger UI bound to your Swagger document. This acts as living documentation for your API hosted from within your app.
MIT License
1.41k stars 225 forks source link

Dynamic swagger docs with load balancing and/or concurrent users #290

Closed tzuge closed 2 years ago

tzuge commented 2 years ago

Love getting express with swagger going fast with this library.

I think there is a defect or at least documentation gap around dynamic docs in production scenarios.

If I understand correctly, swagger-ui-init.js is generated on the first browser request in the handler returned by setup(), then returned on a follow-up request due to the script src, and the state is maintained in the closure over swaggerInit.

app.use('/api-docs', function(req, res, next){
    const { world } = req.query;
    swaggerDocument.info.description = `Hello ${world}!`;
    req.swaggerDoc = swaggerDocument;
    next();
}, swaggerUi.serve, swaggerUi.setup());
  1. In a multi-user scenario, user A requests /api-docs?world=Mars then user B requests /api-docs?world=Venus, user B request would override swaggerInit and user A browser might get swagger-ui-init.js associated with user B request.

  2. In a load balanced scenario, user A requests /api-docs?world=Mars, but the subsequent request for swagger-ui-init.js could go to an instance where swaggerInit has not been updated.

Can the initialization script just be inline with the html returned, or alternatively the swagger ui options could be included in the script src URL for swagger-ui-init.js?

scottie1984 commented 2 years ago

Yep -makes sense. There is another function serveFiles which doesn’t use swaggerInit however it doesn’t use the swaggerDoc from the request. I will look to make a change and update the docs and let you know.

scottie1984 commented 2 years ago

Released: https://github.com/scottie1984/swagger-ui-express/releases/tag/4.4.0

Close for now, please let me know.

akalter commented 1 year ago

Maybe it works, but as you said it doesn’t use the swaggerDoc from the request. In my case we also need two swagger docs.

So every time that user send request to the server, the middleware is modifying the request again.


let origSwagger = require('./../swagger-output.json');
let origSwagger2 = require('./../swagger-output2.json');
let swaggerDocument = Object.assign({}, origSwagger);
let swaggerDocument2 = Object.assign({}, origSwagger2);

const modifySwaggerByHeader = (doc, origDoc) => (req, res, next) => {
    Object.assign(doc, origDoc);
    doc.basePath = req.headers["x-base-prefix"] + doc.basePath;
    req.swaggerDoc = doc;
    next();
}

app.use(config.get("apiUrl") + "/api-docs", modifySwaggerByHeader(swaggerDocument, origSwagger), swaggerUi.serveFiles(swaggerDocument, swaggerUiOptions), swaggerUi.setup())
app.use(config.get("apiUrl") + "/api-docs2", modifySwaggerByHeader(swaggerDocument2, origSwagger2), swaggerUi.serveFiles(swaggerDocument2, swaggerUiOptions), swaggerUi.setup())

In the case from the example, if the x-base-prefix header is "/hello" in the first time the basePath is /hello/api/v1, in the second time is /hello/hello/api/v1. Adding the prefix multiple times.

I solved it using Object.assign to reset the swagger document every time. Any better way to solve it?