Coder Social home page Coder Social logo

mpashkovskiy / express-oas-generator Goto Github PK

View Code? Open in Web Editor NEW
207.0 11.0 36.0 1.18 MB

OpenAPI (Swagger) specification generator for ExpressJS applications

License: Apache License 2.0

JavaScript 100.00%
openapi expressjs swagger swagger-ui mongoose

express-oas-generator's Introduction

express-oas-generator

npm package

Build Status Coverage Status Known Vulnerabilities

Module to:

  • automatically generate OpenAPI (Swagger) specification for existing ExpressJS 4.x REST API applications;
  • provide Swagger UI basing on generated specification.

Examples

How to use

Note - make sure to also read the Advanced usage (recommended) section after this!

  • Install module npm i express-oas-generator --save;
  • Import it in a script where you initialize ExpressJS application (see server_basic.js for usage example);
const express = require('express');
const expressOasGenerator = require('express-oas-generator');
  • Run initialization of module right after instantiating app;
let app = express();
expressOasGenerator.init(app, {}); // to overwrite generated specification's values use second argument.

Second argument of expressOasGenerator.init(app, {}) could be either an object or a function. In case of the object generated spec will be merged with the object. In case of function it will be used to apply changes for generated spec. Example of function usage:

generator.init(app, function(spec) {
    _.set(spec, 'info.title', 'New Title');
    _.set(spec, 'paths[\'/path\'].get.parameters[0].example', 2);
    return spec;
});

To write specification into a file use third and forth (optional) arguments. Full signature of init() function is following:

expressOasGenerator.init(
  app,
  function(spec) { return spec; },
  'path/to/a/file/filename.json',
  60 * 1000,
  'api-docs',
  ['User', 'Student'],
  ['users', 'students'],
  ['production'],
  true,
  SPEC_OUTPUT_FILE_BEHAVIOR.RECREATE
)

where the last parameters are:

  • 'path/to/a/file/filename.json' - (Optional) path to a file and file name, if missing module won't write spec to the disc
  • 60 * 1000 - (Optional) write interval in milliseconds (default: 10 seconds)
  • 'api-docs' - (Optional) Swagger UI path for your REST API (default: api-docs)
  • ['User', 'Student'] - (Optional) Mongoose models to be included as definitions. To get all just do mongoose.modelNames(). The following peer dependencies are required to use this last argument: mongoose, mongoose-to-swagger, bson.
  • ['users', 'students'] - (Optional) Tags: Really useful to group operations (commonly by resources). All the matching paths containing the supplied tags will be grouped. If not supplied, defaults to mongoose models. See example.
  • ['production'] - (Optional) Ignored node environments. Middlewares are not applied when process.env.NODE_ENV is ignored. Existing api-docs and api-spec are still served.
  • true - (Optional) Always serve docs. In case you don't want to apply middelwares but still serve existing api-docs and api-spec.
  • SPEC_OUTPUT_FILE_BEHAVIOR.RECREATE - (Optional) Enum to indicate if the spec outfile file is recreated or preserved from current content (SPEC_OUTPUT_FILE_BEHAVIOR.PRESERVE)

Advanced usage (recommended)

Instead of using a single init handler, we'll use 2 separate ones - one for responses, and one for requests.

let app = express();
/** place handleResponses as the very first middleware */
expressOasGenerator.handleResponses(app, {});

/** initialize your `app` and routes */

/** place handleRequests as the very last middleware */
expressOasGenerator.handleRequests();
app.listen(PORT);

mind the order of the middleware handlers - first we apply the one for responses, then we apply the one for requests, which might seem counter-intuitive since requests come before responses, but this is how we need to do it because:

  • to intercept responses response.write()/end() methods should be wrapped before any route or middleware call it
  • to intercept requests in right format they have to be read after parsing middlewares like body-parser

Don't worry - we'll throw a loud error if you messed this up so that you can correct yourself quickly! πŸ’₯

See server_advanced.js for usage example.

Why do we need to do this?

In order to generate documentation, we need to analyze both responses and requests.

The tricky thing is - one handler must be placed as the very first middleware of the express app, and the other must be the very last. It is needed to intercept all the data (headers and payload) coming in and out out the app.

In the expressOasGenerator.init() method, we assume that you place it straight after initializing the express app. Inside we place response intercept middleware and then we call setTimeout with 1000 miliseconds to make sure we place our request intercept middleware as the very last one.

The basic approach is error-prone because:

  • if you have heavy initialization logic it can take longer than a second, then the request handler will be placed, and it would not be the last middleware of the app.
  • if you want to start using the API as soon as possible requests would not be handled until the 1000 milisecond setTimeout passes and applies the request middleware.

This could occur, for example, if you start your express server and then run the API tests immidiately - that wouldn't work. You'd have to start your server and then make your tests wait a second before the request middleware is applied.

(Optional) Additions to your package.json

If your service is running not at the root of the server add full base path URL to package.json

{
  "baseUrlPath" : "/tokens"
}

Here is a sample

{
  "name": "cwt-sts-svc",
  "version": "1.1.48",
  "description": "JWT generation service",
  "keywords": [],
  "author": "",
  "main": "lib",
  "baseUrlPath" : "/tokens",
  "bin": {
    "cwt-sts-svc": "bin/server"
  }
}

(Optional) Edit Swagger Document Options

This library uses swagger-ui-express as dependency , so if you need to edit the swagger's default documentation page style you can set swaggerDocumentOptions. This option receives any custom swagger options and pass through when swaggerUi are configured.

You can follow these links to see how settings can be edited:

An basic example is:

generator.handleResponses(app, {
    swaggerDocumentOptions: { customCss: '.swagger-ui { background-color: red }' }
  });

And that would result in this: image

Rationale

Goal of the module is to provide developers with Swagger UI in development environments. Module process every request and response therefore it may slow down your app - is not supposed to be used in production environment.

Assuming you have ExpressJS REST API application and you

  • don't want to write documentation manually;
  • but want to use Swagger ecosystem:
    • keep REST API endpoint documented;
    • provide others with Swagger UI to your REST API;
    • generate client libraries for it with Swagger code generator.

How does it work?

  1. During initialization module iterates through all routes and methods and initializes OpenAPI (Swagger) specification.
  2. After an application start module analyze every request/response and fills specification with schemes and examples.
  3. Module replace values of password fields with ******

Limitations

  1. All headers with prefix X- treated as a apiKey type headers;
  2. Module doesn't recognize enumerations in JSON objects;

Troubleshooting

Contributing

Please read:

express-oas-generator's People

Contributors

anodynos avatar dependabot[bot] avatar digitalchiefarchitect avatar kiprasmel avatar kirkeaton avatar landermalta avatar materyze avatar matveypashkovskiy avatar maxiejbe avatar mpashkovskiy avatar snyk-bot avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

express-oas-generator's Issues

SwaggerUI Execute doesn't work

Hi,

I have embedded your code in my NodeJS App (server.js)

When I open swagger-ui , I can see the routes , but when I try to execute the call it doesn't display the response .. (in browser network tab the call is successful and response is received)

Also attaching swagger file which is generated (changed extensions while attaching)

Thanks,
Aniket.

server.log

swagger-spec.log

TypeError: Cannot read property 'forEach' of undefined

Hi man,
I'm trying to use this library but I have the following issue from the console

TypeError: Cannot read property 'forEach' of undefined
index.js:36
    at getEndpoints (/Users/jdnichollsc/dev/enturnate/enturnate-api/node_modules/express-list-endpoints/src/index.js:36:9)
    at /Users/jdnichollsc/dev/enturnate/enturnate-api/node_modules/express-list-endpoints/src/index.js:65:9
    at Array.forEach (<anonymous>)
    at getEndpoints (/Users/jdnichollsc/dev/enturnate/enturnate-api/node_modules/express-list-endpoints/src/index.js:36:9)
    at init (/Users/jdnichollsc/dev/enturnate/enturnate-api/node_modules/express-oas-generator/index.js:37:21)
    at Timeout.setTimeout [as _onTimeout] (/Users/jdnichollsc/dev/enturnate/enturnate-api/node_modules/express-oas-generator/index.js:166:5)
    at ontimeout (timers.js:475:11)
    at tryOnTimeout (timers.js:310:5)
    at Timer.listOnTimeout (timers.js:270:5)

And the app value is:

[[FunctionLocation]]:internal#location
[[Scopes]]:Scopes[3]
arguments:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
caller:TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
length:3
name:""

Let me know what you think

Thanks in advance, Nicholls

No new routes are being written to the file after inital load

Whenever I make requests to new routes I have created they do not appear in the docs. Here is my app.js file. Did I miss order something?

var express = require("express");
const expressOasGenerator = require("express-oas-generator");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
const fs = require("fs");
const mkdirp = require("mkdirp");

var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");

var app = express();

let openAPIFilePath = "./docs/api.json";
/** handle the responses */
if (process.env.NODE_ENV !== "production") {
  /** work-around - https://github.com/mpashkovskiy/express-oas-generator/issues/51 */
  mkdirp.sync(path.parse(openAPIFilePath).dir);

  /** work-around - https://github.com/mpashkovskiy/express-oas-generator/issues/52 */
  let predefinedSpec;

  try {
    predefinedSpec = JSON.parse(
      fs.readFileSync(openAPIFilePath, { encoding: "utf-8" })
    );
  } catch (e) {
    //
  }

  /** work-arounds done. Now handle responses - MUST be the FIRST middleware */
  expressOasGenerator.handleResponses(app, {
    specOutputPath: openAPIFilePath,
    predefinedSpec: predefinedSpec ? () => predefinedSpec : undefined,
  });
}

app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));

app.use("/", indexRouter);
app.use("/users", usersRouter);
expressOasGenerator.handleRequests();
app.listen(3000);
module.exports = app;

Update:

fixed after changing predefinedSpec: predefinedSpec ? () => predefinedSpec : undefined to just predefinedSpec: predefinedSpec ? predefinedSpec : undefined

Is there a way to ignore certain routes or apply any sort of filter?

I love the option for outputting specification to a .json file. However the problem I face is, the file grows so large. Based on my usage pattern, some of the routes get tons of examples added. And some others don't have any yet.

It will be great if we can tightly control what goes into the specification json. Things like, store only 1-2 examples per api.

I believe the second argument to init() is for this purpose. But I am not sure how exactly to tweak it for the above need.

I will be happy to create a documentation PR if anybody can shed some light here.

Doesn't check for compression

I was finding that some of my response schemas looked corrupt. Upon deeper inspection, the buffer assembled from the chunks in updateResponses was compressed (the content-encoding header was gzip). I disabled compression in my dev environment as a workaround.

post payload documentation

Hello,

It is a great tool, but when documenting a Post it is not displaying the payload in the swager doc. How can I do that?

First request never generates response doc

The first request in my projects only creates documentation for the routes, not for the responses. When running the same request more than once, the response is added to the documentation.

(Tried experimenting with writeIntervalMs and other settings, but can't get it to work.)

I've created a test repo demonstrating the issue. This is extra bad since the best use case for express-oas-generator is to use it with unit tests, those promoting both TDD and swagger doc without having to do it manually with annotations etc.

Example repo:
https://github.com/jens-peterolsson/test-express-oas-generator

npm start then http://localhost:5000/data

or just

npm test

Spec is merged with swagger.json.

Function's parameters description

Hello!
First I'd like to congratulate you for your good work with this project!
And here we go: I actually can't manage to show in the Swagger's UI the description of each parameter... Is there specific way of do it?

Thank you in advance!

Cannot read property path

During npm start.

I get:

..../node_modules/express-oas-generator/index.js:44
      let path = prefix + route.route.path;
                                      ^

TypeError: Cannot read property 'path' of undefined
    at stack.forEach.route (..../node_modules/express-oas-generator/index.js:44:39)
    at Array.forEach (<anonymous>)
    at app._router.stack.forEach.router (..../node_modules/express-oas-generator/index.js:42:11)
    at Array.forEach (<anonymous>)
    at init (..../node_modules/express-oas-generator/index.js:34:21)
    at Timeout.setTimeout [as _onTimeout] (..../node_modules/express-oas-generator/index.js:162:5)
    at ontimeout (timers.js:458:11)
    at tryOnTimeout (timers.js:296:5)
    at Timer.listOnTimeout (timers.js:259:5)

Migrate to travis-ci.com

They are shutting down travis-ci.org and thus all the projects have to migrate to travis-ci.com.

From official email:

We encourage you to migrate your existing repositories that are currently on travis-ci.org over to travis-ci.com as soon as possible, enabling you to identify any additional changes required to your code or configuration well in advance of the December 31st deadline. We’ve documented everything you need to know in order to migrate your repositories, and of course, you can reach out on the community forum for any additional help.

Automatically load & save api-spec.json

It would be great if we had a config as 3rd param, where we can specify various settings, one of them being the path of the currently generated spec, so it can be loaded and saved.

Right now we're using it as:

expressOasGenerator.init(expressApp, require('../api-docs/api-spec.json'))

which means we have to manually update it via copy-paste when it changes.

With the new proposal it would look similar to:

expressOasGenerator.init(expressApp, null, {
  currentSpec: '../api-docs/api-spec.json', // auto loads it
  autoSave: true // or it can be a `_.debounce` number, defaulting to 1000ms
)})

Use object for optional arguments

Current init function header:

module.exports.init = (aApp, aPredefinedSpec, aPath, aWriteInterval, aApiDocsPath = 'api-docs') => {

I think that it would make the call of the init function easier when the optional arguments are passed as an object with default params like:

module.exports.init = (aApp, aPredefinedSpec, { aPath, aWriteInterval, aApiDocsPath } = { aPath: '', aWriteInterval: 10 * 1000, aApiDocsPath: 'api-docs'}) => {...}

swagger ui api response not able to update at a time

Hi,

i am working with swagger for node api documentation with express-oas-generator

my main problem as every thing working for me until api call
i made api call its giving response it also updated in swagger.json file but at a run time the response is not shown in swagger ui
after clicking execute button i got response but not update in swagger ui

"express-oas-generator": "^1.0.7",
"swagger-ui-express": "^4.0.6",

Capture

Capture

Support for mongoose model definitions

In order to have mongoose model definitions, it could be pretty useful to integrate a library such as:

Matching route requests/responses with model definitions must be hard to implement.
Just a read-only implementation would be great!

An interface like this could work:

expressOasGenerator.handleResponses(server, {
    specMongooseModelsPath: './models',
    specMongooseIncludeModels: ['User', 'Blog', ...],    
});

Thanks in advance!

The new `handleResponses` and `handleRequests` method does work

I was trying to implement the new functionality, provided by #39.

It wouldn't work.

I found out what was wrong, here's a quick patch (solves the issue, but unfinished yet): I thought this solved the issue, but it did not. I'm a little lost now.

diff --git a/node_modules/express-oas-generator/index.js b/node_modules/express-oas-generator/index.js
index 753ce13..4d5a3ec 100644
--- a/node_modules/express-oas-generator/index.js
+++ b/node_modules/express-oas-generator/index.js
@@ -276,7 +276,7 @@ function handleResponses(expressApp, options = { pathToOutputFile: undefined, wr
  *
  * @returns void
  */
-function handleRequests(options = { path: 'api-docs', predefinedSpec: {} }) {
+function handleRequests(expressApp, options = { path: 'api-docs', predefinedSpec: {} }) {
   /** make sure the middleware placement order (by the user) is correct */
   if (responseMiddlewareHasBeenApplied !== true) {
     throw new Error(WRONG_MIDDLEWARE_ORDER_ERROR);
@@ -286,7 +286,7 @@ function handleRequests(options = { path: 'api-docs', predefinedSpec: {} }) {
   responseMiddlewareHasBeenApplied = false;
 
   /** middleware to handle REQUESTS */
-  app.use((req, res, next) => {
+  expressApp.use((req, res, next) => {
     try {
       const methodAndPathKey = getMethod(req);
       if (methodAndPathKey && methodAndPathKey.method && methodAndPathKey.pathKey) {

What was the problem is that I thought that the global app variable would hold a reference to the app we pass in from the first handler - handleResponses.

Thus I was not asking for the app variable in the second handler - handleRequests and just used the global one.

That did not work.
As seen in the patch above - after receiving the expressApp variable inside the handleRequests function -- everything worked fine.


My question is -- do we need the global app variable?

if yes, then how would we go about it at the handleRequests handler - override the global app variable with the passed in expressApp variable?

(That would probably make sense because it's the latest one), but do we need the global variable anyway then? (I haven't looked if it's needed for anything else yet - sorry:D)


I currently cannot get the new setup to work, so that needs something done too.


also, we currently are NOT passing in the app variable into handleRequests from the init function - and it still works fine.
I don't know what's up.

Also, currently the docs state that you need to pass in the app variable into the handleRequests handler - that's not true, because it accepts only an options object. We'll update that later too.

Deprecate init. Preserve only advanced usage.

Init won't be used anymore, need to ship a major version.
Only advanced usage (from now) will be valid: handleResponses and handleRequests middlewares.
Adjusting README.md + examples accordingly.

Parameters and response-body not documented while using express-oas-generator

Thanks for the library. I am using version 1.0.7
Below is my app.js code here i am using express-oas-generator

var express = require('express');
var app = express();
const expressOasGenerator = require('express-oas-generator');

var bodyParser = require('body-parser');
var port = process.env.PORT || 3606;
var db = 'mongodb://localhost/example';

expressOasGenerator.init(app, {});
var books = require('./routes/books');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

app.use('/books', books);

app.listen(port, function(){
    console.log('app listening on port: '+port);
});

Below is my schema:

var BookSchema = new Schema({
	title: String,
	author: String,
	category: String,
	booknames: [ String],
	booktypes: [{
        _id : false,
        lang_code:{type : String ,required : true },
        booktype: String,
    }]
});

My api-spec looks like below:

paths: {
     /books: {
           get: {
                  summary: "/books",
                 consumes: [
                        "application/json"
                 ],
                 parameters: [ ]
           },
        post: {
                summary: "/books",
                consumes: [
                    "application/json"
                ],
               parameters: [ ]
        }
},

My post api looks like below:

router.post('/', async(req, res, next)=>{
    res.setHeader('Content-Type', 'application/json');
    var newBook = new Book();
    newBook.set(req.body)
    await newBook.save(function(err, book){
        if(err) {
            res.status(HttpStatus.BAD_REQUEST)
            .send({
                error: HttpStatus.getStatusText(HttpStatus.BAD_REQUEST)
            });
        } 
    });
    next();
});

I have seen the similar issues and implemented in the similar way. Please help where I am doing wrong.
https://github.com/mpashkovskiy/express-oas-generator/issues/9
https://github.com/mpashkovskiy/express-oas-generator/issues/4

Body params not being picked up

prices.js

const express = require("express");
const router = express.Router();
const { soap } = require("strong-soap");
const urlv6 = "./wsdl/series_data_v6.WSDL";
const clientPromise = new Promise((resolve, reject) =>
soap.createClient(urlv6, {}, (err, client) =>
  err ? reject(err) : resolve(client)
)
);

router.post("/", function(req, res, next) {
clientPromise
  .then(client => ({ client, request: req.body }))
  .then(invokeOperations)
  .then(results =>{
    res.json(results);
    next();
  })
  .catch(({ message: error }) => {
    res.json(error);
    next(error);
  });
});

const invokeOperations = ({ client, request }) =>
new Promise((resolve, reject) => {
  client.setSecurity(
    new soap.BasicAuthSecurity(process.env.SEB_USER, process.env.SEB_PASSWORD)
  );
  client.RequestSeriesData(request, (err, result) => {
    err ? reject(err) : resolve(result);
  });
});

module.exports = router;

I get the following error Error: Can't set headers after they are sent.
I read the other issues and i noticed if I donΒ΄t call next() then no parameters will be picked up but when i use next() it complains about my header being set.

request bodies are not captured

when implementing this tool i have noticed that request bodies are not captured.
can someone shed some light as to why this might be the case ?

Unexpected token in JSON at position 0

Sometimes my REST API fails with this error:

/usr/src/bff/node_modules/express-oas-generator/lib/processors.js:122
  const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
                    ^
SyntaxError: Unexpected token  in JSON at position 0
    at JSON.parse (<anonymous>)
    at updateResponses (/usr/src/bff/node_modules/express-oas-generator/lib/processors.js:122:21)
    at ServerResponse.res.end (/usr/src/bff/node_modules/express-oas-generator/lib/processors.js:156:9)
    at Gzip.onStreamEnd (/usr/src/bff/node_modules/compression/index.js:212:14)
    at emitNone (events.js:111:20)
    at Gzip.emit (events.js:208:7)
    at endReadableNT (_stream_readable.js:1064:12)
    at _combinedTickCallback (internal/process/next_tick.js:138:11)
    at process._tickCallback (internal/process/next_tick.js:180:9)
npm ERR! code ELIFECYCLE

I have not understood how to reproduce it yet, but may be a trim and try-catch would be helpful here.

multiple get routes are not considered with in the same parent route

TypeScript code base:
I have routes as follows
this.router.get('/list', this.userCtrl.fetchAllUsers);
this.router.get('/:id', this.userCtrl.fetchById);
this.router.post('/create', this.userCtrl.createUser);
this.router.put('/:id', this.userCtrl.updateById);
this.router.delete('/:id', this.userCtrl.deleteById);
this.router.patch('/:id', this.userCtrl.fetchById);

parent route is defined as follows
this.app.use('/api/user', new UserRoute().getUserRoutes());

When i open the http://localhost:3000/api-docs

  1. swagger-ui does not show /:id GET route at all
  2. PATCH request is never shown

Can't reach localhost:3000/api-docs

Hi guys! I've got a problem: I can't reach localhost:3000/api-docs: it gives me "ERR_TOO_MANY_REDIRECTS".

This is how I init module from "ExpressServer.js", the file that starts my express server (app) and swaggerDocument is json file that wraps all specs.
expressOasGenerator.init(app, swaggerDocument);

Any hint of why this is happening?

Thank in advance.

Remove unused tags

Sometimes it happens that certain tags don't match any route. Remove them from docs.

Plus: Pluralize mongoose model tags to check matches.

Open Api 3 spec?

Is there plans in the roadmap to add generation of the open api 3 spec?

predefinedSpec is not a function

Hi when I try to use your generator I get : predefinedSpec is not a function.

I"m not doing anything fancy... just the plain example :

  expressOasGenerator.init(this.app);

I'm using "this" because my code is in typescript.

any idea ?

`Error: Can't set headers after they are sent.` when using `next()` in production

I used to have route handlers like this:

router.get("/", async (_req, res) => {
	try {
		return res.json({ foo: "bar" });
	} catch (err) {
		console.error(err);
		return res.status(500).json({ foo: "", error: err });
	}
});

and after reading #24 and #29, I've started using next:

-router.get("/", async (_req, res) => {
+router.get("/", async (_req, res, next) => {
	try {
-		return res.json({ foo: "bar" });
+		res.json({ foo: "bar" });
+		return next();
	} catch (err) {
		console.error(err);
-		return res.status(500).json({ foo: "", error: err });
+		res.status(500).json({ foo: "", error: err });
+		return next(err);
	}
});

This seems to work fine in development, but when using the API in production, I get errors like this:

Error: Can't set headers after they are sent.
    at SendStream.headersAlreadySent (/home/kipras/projects/turbo-schedule/node_modules/send/index.js:390:13)
    at SendStream.send (/home/kipras/projects/turbo-schedule/node_modules/send/index.js:617:10)
    at onstat (/home/kipras/projects/turbo-schedule/node_modules/send/index.js:729:10)
    at FSReqCallback.oncomplete (fs.js:159:5)

If I revert back to how I was using the route handlers without calling next OR only calling next if the response body/headers were NOT modified, everything works fine & I don't get the errors anymore.

This answer provides some useful information: https://stackoverflow.com/a/7789131

also, from the same thread: https://stackoverflow.com/a/7086621


What solves the issue is only calling next() when the NODE_ENV !== "production", which makes the code look terrible and I'd love to avoid this:D

router.get("/", async (_req, res, next) => {
	try {
		res.json({ foo: "bar" });
+		if (process.env.NODE_ENV !== "production") {
-		return next();
+			return next();
+		}
+
+		return;
	} catch (err) {
		console.error(err);
		res.status(500).json({ foo: "", error: err });
+		if (process.env.NODE_ENV !== "production") {
-		return next(err);
+			return next(err);
+		}
+
+		return;
	}

Is there possibly any way to work around this?


I only really use the API spec generation once building:

I start up the server,
init expressOasGenerator,
activate API routes so that the docs get generated
& stop the server.

Then, once I have the openAPI.json file, I serve it in production through another package from npm (I use redoc, one could also use swagger-ui), because the file is just static, it does not need updating etc.


TL;DR:

The errors shouldn't be there. The next() also should not be called in these places, as noted in the stackoverflow threads above.
Working around with process.env.NODE_ENV works, but it's just wrong and I don't want to have my stuff like this.

Once again, I'd love to solve this if possible.

docs return in json format

the header must be set to html in the serveApiDocs method like this:

  app.use(packageInfo.baseUrlPath + '/' + swaggerUiServePath, swaggerUi.serve, (req, res) => {
    res.setHeader('Content-Type', 'text/html');
    swaggerUi.setup(patchSpec(predefinedSpec))(req, res);
  });

About the requesting body

express-oas-generator is the salvation of my situation right now. Thank you
I have a question.
Is there a way to request a definition for the request body? or
Is there a way to predefine the response?
Or is there any possibility to add to that function?

Thank you again

Prevent host and schemes fields from getting generated

Thanks for this awesome module.

Is there a way to not generate host and schemes in the spec?

We are trying to generate the spec during the build/test phase, package and use it in all environments. But because of the generated host value, swagger does not seem to pick the url dynamically (based on the environment url where it is served from).

I tried the following for removing the host but i still get the host generated as localhost:8080.

generator.handleResponses(app, {
    predefinedSpec: spec => {
      _.set(spec, "info.title", "Test API");
      _.set(spec, "info.description", "Test Description");
      _.set(spec, "host", "");
      return spec;
    },
    writeIntervalMs: 3000,
    specOutputPath: "./server/swagger_admin.json"
  });

Feature request: Possibility to specify baseUrlPath in the code

First of all, thanks for this GREAT tool! I've alrady given you a star and promoted the tool on stackoverflow. (This was not related to the feature request)

I appreciate the possibility to specify a baseUrlPath in the package.json file.

It would be useful, however, if I could drive the base URL using an environment variable during Docker deployments.

My suggestion is something similar as:

let app = express();
expressOasGenerator.init(app, {
   baseUrlPath: process.env.BASE_URL
});

Any suggestions / objections? Thanks!

Roadmap 1.X + 2.X

Creating this issue as a roadmap (sorted by priority):

Still in 1.X:

  • OpenApi3: PR #63
  • Fix links to specification json: PR #68
  • Warning for production env #64: PR #69
  • Unhandled requests/responses: #50 + #55: PR #73
  • Preserve OpenApi spec file ` #52: PR #86
  • Ensure OpenApi spec file directory #51: PR #91
  • Remove unused tags #72
  • Smaller README.md, focused on "advanced" (unique) usage.
  • Nice refactor: Promises (remove all callbacks), async/await, args destructuring, linter (including examples + tests).

2.X major version:

  • Deprecating init #65

What do you think @kiprasmel @mpashkovskiy ?

Doesn't get body params

At least I don't know how to do it.
Your work is great! Saved a lot of time for my documentation, but the API request body params.
I use postman to run tests.

Manipulate examples

It would be great if the user could manipulate example, before these are added to the spec.

The rationale is that in staging (or production!) mode, the examples that get accumulated might be too big, or too sensitive or otherwise useless and there is no standard way for the library to deal with such issues.

This can be achieved by passing a manipulateExample callback option on init - for example this would trim large arrays, keeping only the first item:

expressOasGenerator.init(expressApp, null, {
  manipulateExample: (example, path, method, statusCode, anythingElseHere?) => {
      if (_.isArray(example) {      // trim large example array
          example = [ example[0] ];
          if (example[0].secretCompanyKey) 
              example[0].secretCompanyKey = 'SOME_SECRET_KEY;
      }
      return example;
  }  
)})

parameters and body not documented when not calling to next function

considering this example (taken from express site):

const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(3000, () => console.log('Example app listening on port 3000!'))

the generator will parse the uri but not the parameters and body unless we explicitly call to next:

const express = require('express')
const app = express()

app.get('/', (req, res, next) => {res.send('Hello World!')); next();}

app.listen(3000, () => console.log('Example app listening on port 3000!'))

I know that this route not have any use of params and body, this is only for the example πŸ˜„

[FR] Create an option to preserve the openAPI spec file instead of overwriting it

Originally from https://github.com/sarpik/turbo-schedule/issues/45

Work-around:

+const fs = require('fs')

/** handle the responses */
if (process.env.NODE_ENV !== 'production') {
  /** work-around - https://github.com/mpashkovskiy/express-oas-generator/issues/51 */
  mkdirp.sync(path.parse(openAPIFilePath).dir);

+ /** work-around - https://github.com/mpashkovskiy/express-oas-generator/issues/52 */
+ let predefinedSpec;

+ try {
+   predefinedSpec = JSON.parse(
+     fs.readFileSync(openAPIFilePath, { encoding: 'utf-8' })
+   );
+ } catch (e) {
+   //
+ }

  /** work-arounds done. Now handle responses - MUST be the FIRST middleware */
  handleResponses(app, {
    specOutputPath: openAPIFilePath,
    writeIntervalMs: 0,
+   predefinedSpec: predefinedSpec ? () => predefinedSpec : undefined,
  });
}

No api endpoints are generated

First of all thank for this library, it will be amazing if it works for us (tried 0.1.16 & 0.1.18).

So we get no endpoint generated, initially we thought because of if (req.url.startsWith('/api-')) { (and ours does start with /api-) so we comment it out. Still no results.

Next, we noticed that the line const pathKeys = Object.keys(spec.paths); always produces an empty array initially, so the getPathKey function always returns undefined and no endpoints get documented.

Could you please help us with this?

License?

I noticed this project doesn't have a license yet.

I've been using https://choosealicense.com/ for a while now and it's great for choosing an OSS license.

For the best availability, I think that the MIT License would be the most appropriate, just like express itself.

Though you can explore the licenses here: https://choosealicense.com/licenses/, or if you want in depth - here: https://choosealicense.com/appendix/. I'd still recommend MIT though:)

I can create a PR.

Edit:

It seems like you've put the ISC license inside package.json - that's great, but I think that you need to place the license's file inside the repository too.

Documentation not being updated when testing api with Jest

I'm not sure how to create the example calls for my API.

While doing it manually with postman worked and it added the examples, I couldn't manage to make it work with a jest test suite using supertest.

Is there a way to do with a test suite instead of having to make the request manually?

If so, can you provide an example?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.