k2k2e6
k2k2e6

Reputation: 520

How can one split API documentation in multiple files using Swagger 2.0

According to Swagger 2.0 specs, it might be possible to do this. I am referencing PathObject using $ref which points to another file. We used to be able to do this nicely using Swagger 1.2. But Swagger-UI does not seem to be able to read the referred PathObject in another file.

Is this part of spec too new and is not yet supported? Is there a way to split each "path"'s documentation into another file?

{
    "swagger": "2.0",
    "basePath": "/rest/json",
    "schemes": [
        "http",
        "https"
    ],
    "info": {
        "title": "REST APIs",
        "description": "desc",
        "version": "1.0"
    },
    "paths": {
        "/time": {
            "$ref": "anotherfile.json"
        }
    }
}

Upvotes: 4

Views: 5854

Answers (4)

deerawan
deerawan

Reputation: 8443

My solution to this problem is using this package below to solve the reference issue

https://www.npmjs.com/package/json-schema-ref-parser

Here is the code snippet when generating the swagger UI using that library. I was using Express.js for my node server.

import express from 'express';
import * as path from 'path';
import refParser from '@apidevtools/json-schema-ref-parser';
import swaggerUi from 'swagger-ui-express';

const port = 3100;
const app = express();

app.get('/', async (req, res) => {
  res.redirect('/api-docs')
});

app.use(
  '/api-docs',
  async function (req: express.Request, res: express.Response, next: express.NextFunction) {
    const schemaFilePath = path.join(__dirname, 'schema', 'openapi.yml');

    try {
      // Resolve $ref in schema
      const swaggerDocument = await refParser.dereference(schemaFilePath);
      (req as any).swaggerDoc = swaggerDocument;

      next();
    } catch (err) {
      console.error(err);
      next(err);
    }
  },
  swaggerUi.serve,
  swaggerUi.setup()
);

app.listen(port, () => console.log(`Local web server listening on port ${port}!`));

Take a look at my Github repository to see how it works

Upvotes: 0

Trendfischer
Trendfischer

Reputation: 7632

To support multiple files, your libraries have to support dereferencing the $ref field. But I would not recommend to deliver the swagger file with unresolved references. Our swagger defintion has around 30-40 files. Delivering them via HTTP/1.1 could slow down any reading application.

Since we are building javascript libs, too, we already had a nodejs based build system using gulp. For the node package manager (npm) you can find some libraries supporting dereferencing to build one big swagger file.

Our base file looks like this (shortened):

swagger: '2.0'
info:
  version: 2.0.0
  title: App
  description: Example
basePath: /api/2
paths:
  $ref: "routes.json"
definitions:
  example:
    $ref: "schema/example.json"

The routes.json is generated from our routing file. For this we use a gulp target implementing swagger-jsdoc like this:

var gulp = require('gulp');
var fs   = require('fs');
var gutil = require('gulp-util');
var swaggerJSDoc = require('swagger-jsdoc');

gulp.task('routes-swagger', [], function (done) {
    var options = {
        swaggerDefinition: {
            info: {
                title: 'Routes only, do not use, only for reference',
                version: '1.0.0',
            },
        },
        apis: ['./routing.php'], // Path to the API docs
    };
    var swaggerSpec = swaggerJSDoc(options);
    fs.writeFile('public/doc/routes.json', JSON.stringify(swaggerSpec.paths, null, "\t"), function (error) {
        if (error) {
            gutil.log(gutil.colors.red(error));
        } else {
            gutil.log(gutil.colors.green("Succesfully generated routes include."));
            done();
        }
    });
});

And for generating the swagger file, we use a build task implementing SwaggerParser like this:

var gulp = require('gulp');
var bootprint = require('bootprint');
var bootprintSwagger = require('bootprint-swagger');
var SwaggerParser = require('swagger-parser');
var gutil = require('gulp-util');
var fs   = require('fs');

gulp.task('swagger', ['routes-swagger'], function () {
    SwaggerParser.bundle('public/doc/swagger.yaml', {
        "cache": {
            "fs": false
        }
    })
    .then(function(api) {
        fs.writeFile('public/doc/swagger.json', JSON.stringify(api, null, "\t"), function (error) {
            if (error) {
                gutil.log(gutil.colors.red(error));
            } else {
                gutil.log("Bundled API %s, Version: %s", gutil.colors.magenta(api.info.title), api.info.version);
            }
        });
    })
    .catch(function(err) {
        gutil.log(gutil.colors.red.bold(err));
    });
});

With this implementation we can maintain a rather large swagger specification and we are not restricted to special programming language or framework implementation, since we define the paths in the comments to the real routing definitions. (Note: The gulp tasks are split in multiple files too.)

Upvotes: 2

Mohsen
Mohsen

Reputation: 65785

You can also use JSON Refs library to resolve such multi-file Swagger spec.

I've written about it in this blog post

There is also this GitHub repo to demonstrate how all of this work.

Upvotes: 0

Ron
Ron

Reputation: 14810

While it would theoretically be possible to do that in the future, the solution is still not fully baked into the supporting tools so for now I'd highly recommend keeping it in one file.

If you're looking for a way to manage and navigate the Swagger definition, I'd recommend using the YAML format of the spec, where you can add comments and that may ease up navigation and splitting of a large definition.

Upvotes: 0

Related Questions