Swagger documentation¶
Swagger API documentation is automatically generated and available from your API’s root URL. You can configure the documentation using the @api.doc()
decorator.
Documenting with the @api.doc()
decorator¶
The api.doc()
decorator allows you to include additional information in the documentation.
You can document a class or a method:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
def get(self, id):
return {}
@api.doc(responses={403: 'Not Authorized'})
def post(self, id):
api.abort(403)
Automatically documented models¶
All models instantiated with model()
, clone()
and inherit()
will be automatically documented in your Swagger specifications.
The inherit()
method will register both the parent and the child in the Swagger models definitions:
parent = api.model('Parent', {
'name': fields.String,
'class': fields.String(discriminator=True)
})
child = api.inherit('Child', parent, {
'extra': fields.String
})
The above configuration will produce these Swagger definitions:
{
"Parent": {
"properties": {
"name": {"type": "string"},
"class": {"type": "string"}
},
"discriminator": "class",
"required": ["class"]
},
"Child": {
"allOf": [
{
"$ref": "#/definitions/Parent"
}, {
"properties": {
"extra": {"type": "string"}
}
}
]
}
}
The @api.marshal_with()
decorator¶
This decorator works like the raw marshal_with()
decorator
with the difference that it documents the methods.
The optional parameter code
allows you to specify the expected HTTP status code (200 by default).
The optional parameter as_list
allows you to specify whether or not the objects are returned as a list.
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>', endpoint='my-resource')
class MyResource(Resource):
@api.marshal_with(resource_fields, as_list=True)
def get(self):
return get_objects()
@api.marshal_with(resource_fields, code=201)
def post(self):
return create_object(), 201
The Api.marshal_list_with()
decorator is strictly equivalent to Api.marshal_with(fields, as_list=True)()
.
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>', endpoint='my-resource')
class MyResource(Resource):
@api.marshal_list_with(resource_fields)
def get(self):
return get_objects()
@api.marshal_with(resource_fields)
def post(self):
return create_object()
The @api.expect()
decorator¶
The @api.expect()
decorator allows you to specify the expected input fields.
It accepts an optional boolean parameter validate
indicating whether the payload should be validated.
The validation behavior can be customized globally either
by setting the RESTX_VALIDATE
configuration to True
or passing validate=True
to the API constructor.
The following examples are equivalent:
Using the
@api.expect()
decorator:
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
@api.expect(resource_fields)
def get(self):
pass
Using the
api.doc()
decorator:
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
@api.doc(body=resource_fields)
def get(self):
pass
You can specify lists as the expected input:
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
@api.expect([resource_fields])
def get(self):
pass
You can use RequestParser
to define the expected input:
parser = api.parser()
parser.add_argument('param', type=int, help='Some param', location='form')
parser.add_argument('in_files', type=FileStorage, location='files')
@api.route('/with-parser/', endpoint='with-parser')
class WithParserResource(restx.Resource):
@api.expect(parser)
def get(self):
return {}
Validation can be enabled or disabled on a particular endpoint:
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
# Payload validation disabled
@api.expect(resource_fields)
def post(self):
pass
# Payload validation enabled
@api.expect(resource_fields, validate=True)
def post(self):
pass
An example of application-wide validation by config:
app.config['RESTX_VALIDATE'] = True
api = Api(app)
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
# Payload validation enabled
@api.expect(resource_fields)
def post(self):
pass
# Payload validation disabled
@api.expect(resource_fields, validate=False)
def post(self):
pass
An example of application-wide validation by constructor:
api = Api(app, validate=True)
resource_fields = api.model('Resource', {
'name': fields.String,
})
@api.route('/my-resource/<id>')
class MyResource(Resource):
# Payload validation enabled
@api.expect(resource_fields)
def post(self):
pass
# Payload validation disabled
@api.expect(resource_fields, validate=False)
def post(self):
pass
Documenting with the @api.response()
decorator¶
The @api.response()
decorator allows you to document the known responses
and is a shortcut for @api.doc(responses='...')
.
The following two definitions are equivalent:
@api.route('/my-resource/')
class MyResource(Resource):
@api.response(200, 'Success')
@api.response(400, 'Validation Error')
def get(self):
pass
@api.route('/my-resource/')
class MyResource(Resource):
@api.doc(responses={
200: 'Success',
400: 'Validation Error'
})
def get(self):
pass
You can optionally specify a response model as the third argument:
model = api.model('Model', {
'name': fields.String,
})
@api.route('/my-resource/')
class MyResource(Resource):
@api.response(200, 'Success', model)
def get(self):
pass
The @api.marshal_with()
decorator automatically documents the response:
model = api.model('Model', {
'name': fields.String,
})
@api.route('/my-resource/')
class MyResource(Resource):
@api.response(400, 'Validation error')
@api.marshal_with(model, code=201, description='Object created')
def post(self):
pass
You can specify a default response sent without knowing the response code:
@api.route('/my-resource/')
class MyResource(Resource):
@api.response('default', 'Error')
def get(self):
pass
The @api.route()
decorator¶
You can provide class-wide documentation using the doc
parameter of Api.route()
. This parameter accepts the same values as the Api.doc()
decorator.
For example, these two declarations are equivalent:
Using
@api.doc()
:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
def get(self, id):
return {}
Using
@api.route()
:
@api.route('/my-resource/<id>', endpoint='my-resource', doc={'params':{'id': 'An ID'}})
class MyResource(Resource):
def get(self, id):
return {}
Multiple Routes per Resource¶
Multiple Api.route()
decorators can be used to add multiple routes for a Resource
.
The doc
parameter provides documentation per route.
For example, here the description
is applied only to the /also-my-resource/<id>
route:
@api.route("/my-resource/<id>")
@api.route(
"/also-my-resource/<id>",
doc={"description": "Alias for /my-resource/<id>"},
)
class MyResource(Resource):
def get(self, id):
return {}
Here, the /also-my-resource/<id>
route is marked as deprecated:
@api.route("/my-resource/<id>")
@api.route(
"/also-my-resource/<id>",
doc={
"description": "Alias for /my-resource/<id>, this route is being phased out in V2",
"deprecated": True,
},
)
class MyResource(Resource):
def get(self, id):
return {}
Documentation applied to the Resource
using Api.doc()
is shared amongst all
routes unless explicitly overridden:
@api.route("/my-resource/<id>")
@api.route(
"/also-my-resource/<id>",
doc={"description": "Alias for /my-resource/<id>"},
)
@api.doc(params={"id": "An ID", description="My resource"})
class MyResource(Resource):
def get(self, id):
return {}
Here, the id
documentation from the @api.doc()
decorator is present in both routes,
/my-resource/<id>
inherits the My resource
description from the @api.doc()
decorator and /also-my-resource/<id>
overrides the description with Alias for /my-resource/<id>
.
Routes with a doc
parameter are given a unique Swagger operationId
. Routes without
doc
parameter have the same Swagger operationId
as they are deemed the same operation.
Documenting the fields¶
Every Flask-RESTX field accepts optional arguments used to document the field:
required
: a boolean indicating if the field is always set (default:False
)description
: some details about the field (default:None
)example
: an example to use when displaying (default:None
)
There are also field-specific attributes:
- The
String
field accepts the following optional arguments: enum
: an array restricting the authorized values.min_length
: the minimum length expected.max_length
: the maximum length expected.pattern
: a RegExp pattern used to validate the string.
- The
- The
Integer
,Float
andArbitrary
fields accept the following optional arguments: min
: restrict the minimum accepted value.max
: restrict the maximum accepted value.exclusiveMin
: ifTrue
, minimum value is not in allowed interval.exclusiveMax
: ifTrue
, maximum value is not in allowed interval.multiple
: specify that the number must be a multiple of this value.
- The
The
DateTime
field accepts themin
,max
,exclusiveMin
andexclusiveMax
optional arguments. These should be dates or datetimes (either ISO strings or native objects).
my_fields = api.model('MyModel', {
'name': fields.String(description='The name', required=True),
'type': fields.String(description='The object type', enum=['A', 'B']),
'age': fields.Integer(min=0),
})
Documenting the methods¶
Each resource will be documented as a Swagger path.
Each resource method (get
, post
, put
, delete
, path
, options
, head
)
will be documented as a Swagger operation.
You can specify a unique Swagger operationId
with the id
keyword argument:
@api.route('/my-resource/')
class MyResource(Resource):
@api.doc(id='get_something')
def get(self):
return {}
You can also use the first argument for the same purpose:
@api.route('/my-resource/')
class MyResource(Resource):
@api.doc('get_something')
def get(self):
return {}
If not specified, a default operationId
is provided with the following pattern:
{{verb}}_{{resource class name | camelCase2dashes }}
In the previous example, the default generated operationId
would be get_my_resource
.
You can override the default operationId
generator by providing a callable for the default_id
parameter.
This callable accepts two positional arguments:
The resource class name
The HTTP method (lower-case)
def default_id(resource, method):
return ''.join((method, resource))
api = Api(app, default_id=default_id)
In the previous example, the generated operationId
would be getMyResource
.
Each operation will automatically receive the namespace tag. If the resource is attached to the root API, it will receive the default namespace tag.
Method parameters¶
Parameters from the URL path are documented automatically.
You can provide additional information using the params
keyword argument of the api.doc()
decorator:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
pass
or by using the api.param
shortcut decorator:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.param('id', 'An ID')
class MyResource(Resource):
pass
Input and output models¶
You can specify the serialized output model using the model
keyword argument of the api.doc()
decorator.
For POST
and PUT
methods, use the body
keyword argument to specify the input model.
my_model = api.model('MyModel', {
'name': fields.String(description='The name', required=True),
'type': fields.String(description='The object type', enum=['A', 'B']),
'age': fields.Integer(min=0),
})
class Person(fields.Raw):
def format(self, value):
return {'name': value.name, 'age': value.age}
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
@api.doc(model=my_model)
def get(self, id):
return {}
@api.doc(model=my_model, body=Person)
def post(self, id):
return {}
If both body
and formData
parameters are used, a SpecsError
will be raised.
Models can also be specified with a RequestParser
.
parser = api.parser()
parser.add_argument('param', type=int, help='Some param', location='form')
parser.add_argument('in_files', type=FileStorage, location='files')
@api.route('/with-parser/', endpoint='with-parser')
class WithParserResource(restx.Resource):
@api.expect(parser)
def get(self):
return {}
Note
The decoded payload will be available as a dictionary in the payload attribute in the request context.
@api.route('/my-resource/')
class MyResource(Resource):
def get(self):
data = api.payload
Note
Using RequestParser
is preferred over the api.param()
decorator
to document form fields as it also perform validation.
Headers¶
You can document response headers with the @api.header()
decorator shortcut.
@api.route('/with-headers/')
@api.header('X-Header', 'Some class header')
class WithHeaderResource(restx.Resource):
@api.header('X-Collection', type=[str], collectionType='csv')
def get(self):
pass
If you need to specify an header that appear only on a given response, just use the @api.response headers parameter.
@api.route('/response-headers/')
class WithHeaderResource(restx.Resource):
@api.response(200, 'Success', headers={'X-Header': 'Some header'})
def get(self):
pass
Documenting expected/request headers is done through the @api.expect decorator
parser = api.parser()
parser.add_argument('Some-Header', location='headers')
@api.route('/expect-headers/')
@api.expect(parser)
class ExpectHeaderResource(restx.Resource):
def get(self):
pass
Cascading¶
Method documentation takes precedence over class documentation, and inherited documentation takes precedence over parent documentation.
For example, these two declarations are equivalent:
Class documentation is inherited by methods:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.params('id', 'An ID')
class MyResource(Resource):
def get(self, id):
return {}
Class documentation is overridden by method-specific documentation:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.param('id', 'Class-wide description')
class MyResource(Resource):
@api.param('id', 'An ID')
def get(self, id):
return {}
You can also provide method-specific documentation from a class decorator. The following example will produce the same documentation as the two previous examples:
@api.route('/my-resource/<id>', endpoint='my-resource')
@api.params('id', 'Class-wide description')
@api.doc(get={'params': {'id': 'An ID'}})
class MyResource(Resource):
def get(self, id):
return {}
Marking as deprecated¶
You can mark resources or methods as deprecated with the @api.deprecated
decorator:
# Deprecate the full resource
@api.deprecated
@api.route('/resource1/')
class Resource1(Resource):
def get(self):
return {}
# Deprecate methods
@api.route('/resource4/')
class Resource4(Resource):
def get(self):
return {}
@api.deprecated
def post(self):
return {}
def put(self):
return {}
Hiding from documentation¶
You can hide some resources or methods from documentation using any of the following:
# Hide the full resource
@api.route('/resource1/', doc=False)
class Resource1(Resource):
def get(self):
return {}
@api.route('/resource2/')
@api.doc(False)
class Resource2(Resource):
def get(self):
return {}
@api.route('/resource3/')
@api.hide
class Resource3(Resource):
def get(self):
return {}
# Hide methods
@api.route('/resource4/')
@api.doc(delete=False)
class Resource4(Resource):
def get(self):
return {}
@api.doc(False)
def post(self):
return {}
@api.hide
def put(self):
return {}
def delete(self):
return {}
Note
Namespace tags without attached resources will be hidden automatically from the documentation.
Expose vendor Extensions¶
Swaggers allows you to expose custom vendor extensions and you can use them in Flask-RESTX with the @api.vendor decorator.
It supports both extensions as dict or kwargs and perform automatique x- prefix:
@api.route('/vendor/')
@api.vendor(extension1='any authorized value')
class Vendor(Resource):
@api.vendor({
'extension-1': {'works': 'with complex values'},
'x-extension-3': 'x- prefix is optional',
})
def get(self):
return {}
Export Swagger specifications¶
You can export the Swagger specifications for your API:
from flask import json
from myapp import api
print(json.dumps(api.__schema__))
Swagger UI¶
By default flask-restx
provides Swagger UI documentation, served from the root URL of the API.
from flask import Flask
from flask_restx import Api, Resource, fields
app = Flask(__name__)
api = Api(app, version='1.0', title='Sample API',
description='A sample API',
)
@api.route('/my-resource/<id>')
@api.doc(params={'id': 'An ID'})
class MyResource(Resource):
def get(self, id):
return {}
@api.response(403, 'Not Authorized')
def post(self, id):
api.abort(403)
if __name__ == '__main__':
app.run(debug=True)
If you run the code below and visit your API’s root URL (http://localhost:5000) you can view the automatically-generated Swagger UI documentation.
Customization¶
You can control the Swagger UI path with the doc
parameter (defaults to the API root):
from flask import Flask, Blueprint
from flask_restx import Api
app = Flask(__name__)
blueprint = Blueprint('api', __name__, url_prefix='/api')
api = Api(blueprint, doc='/doc/')
app.register_blueprint(blueprint)
assert url_for('api.doc') == '/api/doc/'
You can specify a custom validator URL by setting config.SWAGGER_VALIDATOR_URL
:
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
app.config.SWAGGER_VALIDATOR_URL = 'http://domain.com/validator'
api = Api(app)
You can enable [OAuth2 Implicit Flow](https://oauth.net/2/grant-types/implicit/) for retrieving an
authorization token for testing api endpoints interactively within Swagger UI.
The config.SWAGGER_UI_OAUTH_CLIENT_ID
and authorizationUrl
and scopes
will be specific to your OAuth2 IDP configuration.
The realm string is added as a query parameter to authorizationUrl and tokenUrl.
These values are all public knowledge. No client secret is specified here.
.. Using PKCE instead of Implicit Flow depends on https://github.com/swagger-api/swagger-ui/issues/5348
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
app.config.SWAGGER_UI_OAUTH_CLIENT_ID = 'MyClientId'
app.config.SWAGGER_UI_OAUTH_REALM = '-'
app.config.SWAGGER_UI_OAUTH_APP_NAME = 'Demo'
api = Api(
app,
title=app.config.SWAGGER_UI_OAUTH_APP_NAME,
security={'OAuth2': ['read', 'write']},
authorizations={
'OAuth2': {
'type': 'oauth2',
'flow': 'implicit',
'authorizationUrl': 'https://idp.example.com/authorize?audience=https://app.example.com',
'clientId': app.config.SWAGGER_UI_OAUTH_CLIENT_ID,
'scopes': {
'openid': 'Get ID token',
'profile': 'Get identity',
}
}
}
)
You can also specify the initial expansion state with the config.SWAGGER_UI_DOC_EXPANSION
setting ('none'
, 'list'
or 'full'
):
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
app.config.SWAGGER_UI_DOC_EXPANSION = 'list'
api = Api(app)
By default, operation ID is hidden as well as request duration, you can enable them respectively with:
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
app.config.SWAGGER_UI_OPERATION_ID = True
app.config.SWAGGER_UI_REQUEST_DURATION = True
api = Api(app)
If you need a custom UI,
you can register a custom view function with the documentation()
decorator:
from flask import Flask
from flask_restx import Api, apidoc
app = Flask(__name__)
api = Api(app)
@api.documentation
def custom_ui():
return apidoc.ui_for(api)
Configuring “Try it Out”¶
By default, all paths and methods have a “Try it Out” button for performing API requests in the browser.
These can be disable per method with the SWAGGER_SUPPORTED_SUBMIT_METHODS
configuration option,
supporting the same values as the supportedSubmitMethods
Swagger UI parameter.
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
# disable Try it Out for all methods
app.config.SWAGGER_SUPPORTED_SUBMIT_METHODS = []
# enable Try it Out for specific methods
app.config.SWAGGER_SUPPORTED_SUBMIT_METHODS = ["get", "post"]
api = Api(app)
Disabling the documentation¶
To disable Swagger UI entirely, set doc=False
:
from flask import Flask
from flask_restx import Api
app = Flask(__name__)
api = Api(app, doc=False)