Plug and play Swagger UI documentation generator for Flask.
Much like the awesome Swashbuckle.AspNetCore.SwaggerGen
, Flask needs a user-friendly, no-nonsense Swagger generation library that doesn't turn your service code into spaghetti. Inspired by Swashbuckle, swagger-gen
comes packaged with all the necessary dependencies (HTML, CSS, React JS and modules, etc) to run Swagger UI with no modifications to the Flask application required to serve them and generate a beautiful Swagger page for your Flask app.
from swagger_gen.lib.wrappers import swagger_metadata
from swagger_gen.lib.security import BearerAuth
from swagger_gen.swagger import Swagger
from flask import Flask, request
app = Flask(__name__)
@app.route('/api/hello/say', methods=['GET'])
@swagger_metadata(
summary='Sample endpoint',
description='This is a sample endpoint')
def test():
return {'message': 'hello world!'}
swagger = Swagger(
app=app,
title='app')
swagger.configure()
if __name__ == '__main__':
app.run(debug=True, port='5000')
The web content required to display Swagger UI is packaged in a binary and included as a package resource. On configuration, the dependencies are fetched from the package and hooked into the Flask app route definitions, so Flask will serve those static dependencies without requiring them to exist in app space. Serving static files from the Flask web server, for almost any other reason, is not a great choice. However, unless you're worried about heavy load on your Swagger page (if that's the case, you may have a different problem to worry about) it won't be an issue.
At the most basic, without any additional metadata defined on the routes, swagger-gen
will generate a Swagger UI with the route names, segments and methods. The basic configuration is confined to a few parameters on the Swagger
class:
swagger = Swagger(
app=app,
title='azure-gateway',
)
swagger.configure()
This is enough to get a page up and running! Currently, there is no support for version delineation on the routes, although it's certainly a good candidate for future improvements.
@swagger_metadata(
request_model={'message' : 'string'},
summary='An example route',
description='This is an example route, check it out!',
response_model=[(200, 'Success'), (500, 'Error')],
query_params=['first_name', 'last_name'],
security='bearer')
@app.route('/api/test')
def post_query(methods=['POST']):
return something
There are a few things that make generating comprehensive specs interesting with Flask compared to other stacks that have similar packages:
- We don't have a concept of a request and response model, as opposed to a framework like .NET where these things are defined, and samples can be generated from them.
- We also don't explicitly define query parameters, they're parsed at runtime from the request context
- We also don't have response definitions, like what status codes we might return
What we do have:
- The route and any interpolated route segments
- The request methods accepted by the route
- Obviously, we've got the endpoint itself
To include these, other Swagger packages have typically relied on a manual spec file being referenced somewhere, which may be okay in some cases but probably frustrating for most. In the interest of keeping things clean, the duty of collecting route metadata happens in two places.
First is what's been described, the buildup using app
and stealing the required data from the route map. The second part is handled through route decorators, and occurs before the app
instance is available. The decorators run on import, and store the parameters for that route in a global collection. These are totally detatched from the actual routes that are parsed on buildup, and it is the view_function
name that links them back.
Route method (view function) names when hooked in to the flask app directly (@app.route
) must be unique, which is good because we're using that function name as a key to link the metadata and the route data from the decorators and buildup respectively. But a Blueprint
does not require cardinality for the view functions. This is because behind the scenes, Flask is actually storing that view function with the blueprint name as a prefix, i.e. the route get_thing
on blueprint thing_bp
is stored at key thing_bp.get_thing
.
gasp
This is a problem if we have two routes defined across different blueprints with the same name, because we'll be overwriting the previous route's metadata if we only use the function name. Since we're collecting this stuff of the route before we have access to the Flask app
(before it even exists) we need a way to uniquely identify these on both sides, but we only have access to the view function itself when we need to generate a key to do that, which is a problem.
There are two options to handle this, both require having access to the Blueprint
instance:
- The instance can be passed in as a parameter to the decorator, and we'll store that view function with the blueprint prefix, the same way Flask will during the buildup.
- If you keep your blueprints in separate files meaning you don't have more than a single blueprint defined in a single module, it can be parsed implicitly and the instance doesn't need to be passed to
@swagger_metadata
. This is done by fetching the caller stack frame (which is the module that contains the decorated route methods) and pulling theBlueprint
instance from the locals in that context. If you are working with multiple blueprints in a single file, there is no way to know which blueprint belongs to which view function, so theBlueprint
needs to be passed in explicitly. Ifimplicit_blueprints
is enabled and more than oneBlueprint
instances are found in the caller context, the app will throw an exception.
The full list of available metadata options is availabe in the decorator doc string. There are also additional values on the Swagger
constructor that map to additional optional fields on the spec.
Want to contribute? Have an issue? https://github.com/danleonard-nj/swagger-gen