CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-flask-restplus

Fully featured framework for fast, easy and documented API development with Flask

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

documentation.mddocs/

Documentation

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.

Capabilities

Swagger Class

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
        """

Documentation Functions

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
    """

API Documentation Blueprint

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: Apidoc

Documentation Constants

Constants 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
}

Usage Examples

Basic API Documentation

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)

Advanced Documentation Features

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 {}, 201

Custom Documentation Decorators

from 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}
        }

Model Documentation with Examples

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')
})

Response Documentation

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 '', 204

Security Documentation

from 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'}

Export Swagger Specification

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}")

Custom Documentation Templates

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

docs

api-resources.md

documentation.md

error-handling.md

fields.md

index.md

input-validation.md

models-marshalling.md

request-parsing.md

tile.json