Fully featured framework for fast, easy and documented API development with Flask
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Automatic API documentation generation with Swagger/OpenAPI support, including interactive documentation interface and specification export. Flask-RESTPlus provides comprehensive documentation features that integrate seamlessly with API development.
Main class for generating and managing Swagger/OpenAPI documentation.
class Swagger:
def __init__(self, api):
"""
Initialize Swagger documentation generator.
Args:
api (Api): API instance to document
"""
def as_dict(self):
"""
Export the complete Swagger specification as a dictionary.
Returns:
dict: Complete Swagger/OpenAPI specification
"""
def extract_tags(self, api):
"""
Extract tags from API namespaces and resources.
Args:
api (Api): API instance
Returns:
list: List of tag definitions
"""
def extract_resource_doc(self, resource, url, route_doc=None):
"""
Extract documentation from a resource class.
Args:
resource (Resource): Resource class
url (str): Resource URL pattern
route_doc (dict, optional): Additional route documentation
Returns:
dict: Resource documentation
"""
def serialize_resource(self, ns, resource, url, route_doc=None, **kwargs):
"""
Serialize a resource for Swagger documentation.
Args:
ns (Namespace): Resource namespace
resource (Resource): Resource class
url (str): Resource URL pattern
route_doc (dict, optional): Route documentation
**kwargs: Additional serialization options
Returns:
dict: Serialized resource documentation
"""
def serialize_operation(self, doc, method):
"""
Serialize an operation (HTTP method) for Swagger.
Args:
doc (dict): Operation documentation
method (str): HTTP method name
Returns:
dict: Serialized operation documentation
"""
def serialize_definitions(self):
"""
Serialize all model definitions for Swagger.
Returns:
dict: Model definitions dictionary
"""
def serialize_schema(self, model):
"""
Serialize a model schema for Swagger.
Args:
model (Model): Model to serialize
Returns:
dict: Serialized model schema
"""
def register_model(self, model):
"""
Register a model for documentation.
Args:
model (Model): Model to register
Returns:
dict: Model reference
"""
def register_field(self, field):
"""
Register a field for documentation.
Args:
field (Field): Field to register
Returns:
dict: Field documentation
"""Utility functions for Swagger documentation generation and management.
def ref(model):
"""
Create a JSON schema reference for a model.
Args:
model (Model): Model to reference
Returns:
dict: JSON schema reference
"""
def extract_path(path):
"""
Transform Flask URL rules to Swagger path format.
Args:
path (str): Flask URL rule (e.g., '/users/<int:user_id>')
Returns:
str: Swagger path format (e.g., '/users/{user_id}')
"""
def extract_path_params(path):
"""
Extract path parameters from a URL pattern.
Args:
path (str): URL pattern
Returns:
list: List of path parameter definitions
"""
def parse_docstring(obj):
"""
Parse docstrings for API documentation.
Args:
obj: Object with docstring (function, class, etc.)
Returns:
dict: Parsed docstring with summary and description
"""
def is_hidden(resource, route_doc=None):
"""
Check if a resource should be hidden from documentation.
Args:
resource: Resource class or instance
route_doc (dict, optional): Route documentation
Returns:
bool: True if resource should be hidden
"""Flask blueprint for serving API documentation interface.
class Apidoc:
def __init__(self):
"""
Initialize API documentation blueprint.
"""
def register(self, *args, **kwargs):
"""
Register the documentation blueprint with a Flask app.
Args:
*args: Registration arguments
**kwargs: Registration keyword arguments
"""
def swagger_static(filename):
"""
Generate URLs for Swagger static files.
Args:
filename (str): Static file name
Returns:
str: URL for static file
"""
def ui_for(api):
"""
Render SwaggerUI interface for an API.
Args:
api (Api): API instance to document
Returns:
str: Rendered SwaggerUI HTML
"""
# Pre-configured documentation blueprint instance
apidoc: ApidocConstants used in Swagger documentation generation.
PATH_TYPES: dict = {
'int': 'integer',
'float': 'number',
'string': 'string',
'path': 'string',
'uuid': 'string'
}
PY_TYPES: dict = {
int: 'integer',
float: 'number',
str: 'string',
bool: 'boolean',
list: 'array',
dict: 'object'
}
DEFAULT_RESPONSE_DESCRIPTION: str = 'Success'
DEFAULT_RESPONSE: dict = {
'description': DEFAULT_RESPONSE_DESCRIPTION
}from flask import Flask
from flask_restplus import Api, Resource, fields
app = Flask(__name__)
# Initialize API with documentation
api = Api(
app,
version='1.0',
title='My API',
description='A comprehensive API for demonstration',
doc='/docs/', # Documentation endpoint
terms_url='https://example.com/terms',
contact='API Support',
contact_email='support@example.com',
contact_url='https://example.com/support',
license='MIT',
license_url='https://opensource.org/licenses/MIT'
)
# Define models for documentation
user_model = api.model('User', {
'id': fields.Integer(required=True, description='User ID'),
'name': fields.String(required=True, description='Full name'),
'email': fields.String(required=True, description='Email address')
})
@api.route('/users')
class UserList(Resource):
@api.doc('list_users')
@api.marshal_list_with(user_model)
def get(self):
"""List all users"""
return [{'id': 1, 'name': 'John', 'email': 'john@example.com'}]
@api.doc('create_user')
@api.expect(user_model, validate=True)
@api.marshal_with(user_model, code=201)
def post(self):
"""Create a new user"""
return api.payload, 201
if __name__ == '__main__':
app.run(debug=True)from flask_restplus import Api, Namespace, Resource, fields
api = Api()
# Create namespace with detailed documentation
users_ns = api.namespace('users',
description='User management operations',
path='/api/users')
# Define comprehensive models
user_create_model = api.model('UserCreate', {
'name': fields.String(required=True, description='Full name', example='John Doe'),
'email': fields.String(required=True, description='Email address', example='john@example.com'),
'password': fields.String(required=True, description='Password (min 8 characters)')
})
user_response_model = api.model('UserResponse', {
'id': fields.Integer(description='Unique user ID', example=1),
'name': fields.String(description='Full name', example='John Doe'),
'email': fields.String(description='Email address', example='john@example.com'),
'created_at': fields.DateTime(description='Account creation timestamp'),
'active': fields.Boolean(description='Account status', example=True)
})
# Error models
error_model = api.model('Error', {
'message': fields.String(description='Error message'),
'code': fields.String(description='Error code')
})
@users_ns.route('/')
class UserList(Resource):
@api.doc('list_users',
description='Retrieve a paginated list of users')
@api.param('page', 'Page number', type='integer', default=1)
@api.param('per_page', 'Users per page', type='integer', default=10)
@api.param('search', 'Search term for filtering users', type='string')
@api.marshal_list_with(user_response_model)
@api.response(200, 'Users retrieved successfully')
@api.response(400, 'Invalid parameters', error_model)
def get(self):
"""
List users with optional filtering and pagination.
Returns a paginated list of users. Use query parameters to control
the results and filter by search terms.
"""
return []
@api.doc('create_user',
description='Create a new user account')
@api.expect(user_create_model, validate=True)
@api.marshal_with(user_response_model, code=201)
@api.response(201, 'User created successfully')
@api.response(400, 'Validation error', error_model)
@api.response(409, 'Email already exists', error_model)
def post(self):
"""
Create a new user account.
Creates a new user with the provided information. Email addresses
must be unique across the system.
"""
return {}, 201from flask_restplus import Api, Resource, fields
from functools import wraps
api = Api()
def paginated_response(model, description="Paginated response"):
"""Custom decorator for paginated responses."""
pagination_model = api.model('Pagination', {
'page': fields.Integer(description='Current page number'),
'per_page': fields.Integer(description='Items per page'),
'total': fields.Integer(description='Total number of items'),
'pages': fields.Integer(description='Total number of pages')
})
paginated_model = api.model(f'Paginated{model.name}', {
'items': fields.List(fields.Nested(model), description='List of items'),
'pagination': fields.Nested(pagination_model, description='Pagination info')
})
def decorator(f):
@wraps(f)
@api.marshal_with(paginated_model)
@api.response(200, description)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decorator
def authenticated_required():
"""Custom decorator for authenticated endpoints."""
def decorator(f):
@wraps(f)
@api.header('Authorization', 'Bearer token', required=True)
@api.response(401, 'Authentication required')
@api.response(403, 'Insufficient permissions')
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wrapper
return decorator
user_model = api.model('User', {
'id': fields.Integer,
'name': fields.String,
'email': fields.String
})
@api.route('/users')
class UserList(Resource):
@paginated_response(user_model, "List of users")
@authenticated_required()
def get(self):
"""Get paginated list of users (authenticated)"""
return {
'items': [],
'pagination': {'page': 1, 'per_page': 10, 'total': 0, 'pages': 0}
}from flask_restplus import Api, fields
from datetime import datetime
api = Api()
# Complex model with detailed documentation
product_model = api.model('Product', {
'id': fields.Integer(
required=True,
description='Unique product identifier',
example=12345
),
'name': fields.String(
required=True,
description='Product name',
example='Premium Widget',
min_length=1,
max_length=100
),
'price': fields.Fixed(
required=True,
description='Product price in USD',
example=29.99,
decimals=2,
min=0
),
'category': fields.String(
required=True,
description='Product category',
example='electronics',
enum=['electronics', 'clothing', 'books', 'home']
),
'in_stock': fields.Boolean(
description='Whether product is in stock',
example=True,
default=True
),
'tags': fields.List(
fields.String,
description='Product tags for searching',
example=['premium', 'featured', 'sale']
),
'specifications': fields.Raw(
description='Product specifications (flexible object)',
example={
'weight': '2.5 lbs',
'dimensions': '10x8x2 inches',
'warranty': '2 years'
}
),
'created_at': fields.DateTime(
description='Product creation timestamp',
example='2023-01-15T10:30:00Z'
)
})
# Model inheritance for documentation
base_entity_model = api.model('BaseEntity', {
'id': fields.Integer(required=True, description='Unique identifier'),
'created_at': fields.DateTime(description='Creation timestamp'),
'updated_at': fields.DateTime(description='Last update timestamp')
})
user_model = api.inherit('User', base_entity_model, {
'username': fields.String(required=True, description='Username'),
'email': fields.String(required=True, description='Email address'),
'profile': fields.Nested('UserProfile', description='User profile information')
})
profile_model = api.model('UserProfile', {
'first_name': fields.String(description='First name'),
'last_name': fields.String(description='Last name'),
'bio': fields.String(description='User biography'),
'avatar_url': fields.Url(description='Profile picture URL')
})from flask_restplus import Api, Resource, fields
api = Api()
# Define various response models
success_model = api.model('Success', {
'message': fields.String(description='Success message'),
'data': fields.Raw(description='Response data')
})
error_model = api.model('Error', {
'message': fields.String(description='Error message'),
'code': fields.String(description='Error code'),
'details': fields.Raw(description='Additional error details')
})
validation_error_model = api.model('ValidationError', {
'message': fields.String(description='Validation error message'),
'errors': fields.Raw(description='Field-specific validation errors')
})
@api.route('/products/<int:product_id>')
class Product(Resource):
@api.doc('get_product')
@api.response(200, 'Product found', product_model)
@api.response(404, 'Product not found', error_model)
@api.response(500, 'Internal server error', error_model)
def get(self, product_id):
"""Get a product by ID"""
return {}
@api.doc('update_product')
@api.expect(product_model, validate=True)
@api.response(200, 'Product updated', product_model)
@api.response(400, 'Validation error', validation_error_model)
@api.response(404, 'Product not found', error_model)
@api.response(409, 'Product name already exists', error_model)
def put(self, product_id):
"""Update a product"""
return {}
@api.doc('delete_product')
@api.response(204, 'Product deleted')
@api.response(404, 'Product not found', error_model)
@api.response(409, 'Cannot delete product with active orders', error_model)
def delete(self, product_id):
"""Delete a product"""
return '', 204from flask_restplus import Api, Namespace, Resource
# Define security schemes
authorizations = {
'bearer': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization',
'description': 'Bearer token authentication'
},
'api_key': {
'type': 'apiKey',
'in': 'header',
'name': 'X-API-Key',
'description': 'API key authentication'
},
'oauth2': {
'type': 'oauth2',
'flow': 'authorizationCode',
'authorizationUrl': 'https://example.com/oauth/authorize',
'tokenUrl': 'https://example.com/oauth/token',
'scopes': {
'read': 'Read access',
'write': 'Write access',
'admin': 'Admin access'
}
}
}
api = Api(
authorizations=authorizations,
security='bearer' # Default security requirement
)
# Namespace with specific security requirements
admin_ns = api.namespace('admin',
description='Admin operations',
security=['bearer', 'oauth2'])
@admin_ns.route('/users')
class AdminUserList(Resource):
@api.doc('admin_list_users', security='oauth2')
@api.response(200, 'Success')
@api.response(401, 'Authentication required')
@api.response(403, 'Admin access required')
def get(self):
"""List all users (admin only)"""
return []
# Public endpoint (no security)
@api.route('/health')
class Health(Resource):
@api.doc('health_check', security=[]) # Override default security
def get(self):
"""Health check endpoint (public)"""
return {'status': 'healthy'}from flask_restplus import Api
import json
api = Api()
# Export complete Swagger specification
swagger_spec = api.__schema__
print(json.dumps(swagger_spec, indent=2))
# Access specific parts of the specification
print("API Version:", swagger_spec.get('info', {}).get('version'))
print("Available paths:", list(swagger_spec.get('paths', {}).keys()))
print("Defined models:", list(swagger_spec.get('definitions', {}).keys()))
# Save specification to file
with open('api_spec.json', 'w') as f:
json.dump(swagger_spec, f, indent=2)
# Generate OpenAPI 3.0 compatible spec (if supported)
try:
openapi_spec = api.as_dict() # May include OpenAPI 3.0 features
with open('openapi_spec.json', 'w') as f:
json.dump(openapi_spec, f, indent=2)
except Exception as e:
print(f"OpenAPI 3.0 export not supported: {e}")from flask_restplus import Api
from flask import render_template_string
api = Api()
# Custom SwaggerUI template
custom_ui_template = """
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} - API Documentation</title>
<link rel="stylesheet" type="text/css" href="{{ url_for('.static', filename='swagger-ui-bundle.css') }}">
<style>
.swagger-ui .topbar { background-color: #1f4e79; }
.swagger-ui .topbar .download-url-wrapper { display: none; }
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="{{ url_for('.static', filename='swagger-ui-bundle.js') }}"></script>
<script>
const ui = SwaggerUIBundle({
url: '{{ specs_url }}',
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.presets.standalone
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
deepLinking: true,
displayRequestDuration: true,
docExpansion: 'none',
filter: true,
showExtensions: true,
showCommonExtensions: true
});
</script>
</body>
</html>
"""
@api.documentation
def custom_ui():
"""Custom documentation UI"""
return render_template_string(
custom_ui_template,
title=api.title,
specs_url=api.specs_url
)Install with Tessl CLI
npx tessl i tessl/pypi-flask-restplus