Skip to main content

Flask Swagger Integration

Introduction

When developing RESTful APIs with Flask, providing clear and interactive documentation is essential for other developers who will consume your API. Swagger (now known as OpenAPI) is a powerful tool for documenting and visualizing your API endpoints. In this tutorial, you'll learn how to integrate Swagger with your Flask application to create interactive, user-friendly API documentation.

Swagger allows you to:

  • Document your API endpoints in a standardized format
  • Test API endpoints directly from a web interface
  • Generate client SDKs automatically
  • Make your APIs more accessible to others

Prerequisites

Before getting started, make sure you have:

  • Basic knowledge of Flask and RESTful API concepts
  • Python 3.6 or later installed
  • A simple Flask application to work with

Setting Up Flask-Swagger

The most common way to integrate Swagger with Flask is using the flask-swagger-ui package, which provides a Flask blueprint for the Swagger UI and a method for generating Swagger specifications.

Step 1: Install Required Packages

First, let's install the necessary packages:

bash
pip install flask flask-restful flask-swagger-ui apispec marshmallow

Step 2: Create a Basic Flask Application

Let's start with a simple Flask API:

python
from flask import Flask, jsonify, request

app = Flask(__name__)

# Sample data
books = [
{"id": 1, "title": "Flask Web Development", "author": "Miguel Grinberg"},
{"id": 2, "title": "Python Crash Course", "author": "Eric Matthes"}
]

@app.route('/api/books', methods=['GET'])
def get_books():
return jsonify(books)

@app.route('/api/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((book for book in books if book['id'] == book_id), None)
if book:
return jsonify(book)
return jsonify({"error": "Book not found"}), 404

@app.route('/api/books', methods=['POST'])
def add_book():
if not request.json or 'title' not in request.json:
return jsonify({"error": "Invalid data"}), 400

book = {
'id': books[-1]['id'] + 1 if books else 1,
'title': request.json['title'],
'author': request.json.get('author', '')
}
books.append(book)
return jsonify(book), 201

if __name__ == '__main__':
app.run(debug=True)

Integrating Swagger UI with Your Flask Application

Now, let's integrate Swagger UI with our Flask application.

Step 3: Create a Swagger Configuration

We'll create a function to generate the Swagger documentation:

python
from flask_swagger_ui import get_swaggerui_blueprint

# Define the Swagger blueprint
SWAGGER_URL = '/api/docs' # URL for exposing Swagger UI (without trailing '/')
API_URL = '/static/swagger.json' # Our API url (can be a local file or url)

# Call factory function to create our blueprint
swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={ # Swagger UI config overrides
'app_name': "Flask Book API"
}
)

# Register blueprint at URL
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)

Step 4: Create a Swagger Specification File

Now we need to create a Swagger specification file that describes our API. Create a static folder in your project root and add a file called swagger.json:

python
import os
from flask import Flask, jsonify, request, send_from_directory
from flask_swagger_ui import get_swaggerui_blueprint

app = Flask(__name__)

# Create a route for serving the swagger.json file
@app.route('/static/swagger.json')
def swagger_json():
with open('static/swagger.json', 'r') as f:
return jsonify(json.load(f))

Now, create a static folder and add a file named swagger.json with the following content:

json
{
"swagger": "2.0",
"info": {
"title": "Flask Book API",
"description": "API for managing books",
"version": "1.0.0"
},
"basePath": "/api",
"schemes": [
"http"
],
"paths": {
"/books": {
"get": {
"summary": "Returns all books",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "Successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Book"
}
}
}
}
},
"post": {
"summary": "Add a new book",
"produces": [
"application/json"
],
"parameters": [
{
"name": "book",
"in": "body",
"description": "Book object",
"required": true,
"schema": {
"$ref": "#/definitions/Book"
}
}
],
"responses": {
"201": {
"description": "Book created",
"schema": {
"$ref": "#/definitions/Book"
}
},
"400": {
"description": "Invalid input"
}
}
}
},
"/books/{bookId}": {
"get": {
"summary": "Find book by ID",
"produces": [
"application/json"
],
"parameters": [
{
"name": "bookId",
"in": "path",
"description": "ID of book to return",
"required": true,
"type": "integer"
}
],
"responses": {
"200": {
"description": "Successful operation",
"schema": {
"$ref": "#/definitions/Book"
}
},
"404": {
"description": "Book not found"
}
}
}
}
},
"definitions": {
"Book": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"title": {
"type": "string"
},
"author": {
"type": "string"
}
}
}
}
}

Step 5: Update the Main Application File

Now, let's put everything together in the main application file:

python
from flask import Flask, jsonify, request, send_from_directory
from flask_swagger_ui import get_swaggerui_blueprint
import json
import os

app = Flask(__name__)

# Sample data
books = [
{"id": 1, "title": "Flask Web Development", "author": "Miguel Grinberg"},
{"id": 2, "title": "Python Crash Course", "author": "Eric Matthes"}
]

# Swagger configuration
SWAGGER_URL = '/api/docs' # URL for exposing Swagger UI
API_URL = '/static/swagger.json' # URL where our API spec is located

# Create Swagger UI blueprint
swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={
'app_name': "Flask Book API"
}
)

# Register blueprint
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)

# Create a route for serving the swagger.json file
@app.route('/static/swagger.json')
def serve_swagger_spec():
return send_from_directory('static', 'swagger.json')

# API routes
@app.route('/api/books', methods=['GET'])
def get_books():
return jsonify(books)

@app.route('/api/books/<int:book_id>', methods=['GET'])
def get_book(book_id):
book = next((book for book in books if book['id'] == book_id), None)
if book:
return jsonify(book)
return jsonify({"error": "Book not found"}), 404

@app.route('/api/books', methods=['POST'])
def add_book():
if not request.json or 'title' not in request.json:
return jsonify({"error": "Invalid data"}), 400

book = {
'id': books[-1]['id'] + 1 if books else 1,
'title': request.json['title'],
'author': request.json.get('author', '')
}
books.append(book)
return jsonify(book), 201

if __name__ == '__main__':
app.run(debug=True)

Automating Swagger Documentation with Flask-RESTful and ApiSpec

Manually creating swagger.json can be tedious. Let's look at how to automate this process using flask-restful and apispec.

Step 6: Installing Additional Packages

bash
pip install flask-restful apispec

Step 7: Creating a More Advanced Example with Automated Documentation

python
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from marshmallow import Schema, fields
from flask_swagger_ui import get_swaggerui_blueprint

app = Flask(__name__)
api = Api(app)

# Create an APISpec
spec = APISpec(
title="Flask Book API",
version="1.0.0",
openapi_version="3.0.2",
plugins=[FlaskPlugin(), MarshmallowPlugin()]
)

# Define schemas
class BookSchema(Schema):
id = fields.Int(dump_only=True)
title = fields.Str(required=True)
author = fields.Str(required=True)

# Register schemas with spec
spec.components.schema("Book", schema=BookSchema)

# Sample data
books = [
{"id": 1, "title": "Flask Web Development", "author": "Miguel Grinberg"},
{"id": 2, "title": "Python Crash Course", "author": "Eric Matthes"}
]

# Resources
class BookResource(Resource):
def get(self, book_id):
"""Get a book by ID
---
parameters:
- in: path
name: book_id
required: true
schema:
type: integer
responses:
200:
description: Return a book
content:
application/json:
schema: BookSchema
404:
description: Book not found
"""
book = next((book for book in books if book['id'] == book_id), None)
if book:
return book
return {"error": "Book not found"}, 404

class BookListResource(Resource):
def get(self):
"""Get all books
---
responses:
200:
description: Return a list of books
content:
application/json:
schema:
type: array
items: BookSchema
"""
return books

def post(self):
"""Add a new book
---
requestBody:
content:
application/json:
schema: BookSchema
responses:
201:
description: Book created
content:
application/json:
schema: BookSchema
400:
description: Invalid input
"""
if not request.json or 'title' not in request.json:
return {"error": "Invalid data"}, 400

book = {
'id': books[-1]['id'] + 1 if books else 1,
'title': request.json['title'],
'author': request.json.get('author', '')
}
books.append(book)
return book, 201

# Register resources
api.add_resource(BookListResource, '/api/books')
api.add_resource(BookResource, '/api/books/<int:book_id>')

# Register paths with spec
with app.test_request_context():
spec.path(view=BookListResource, app=app)
spec.path(view=BookResource, app=app)

# Serve OpenAPI specification
@app.route('/api/swagger.json')
def create_swagger_spec():
return jsonify(spec.to_dict())

# Configure Swagger UI
SWAGGER_URL = '/api/docs'
API_URL = '/api/swagger.json'

swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={
'app_name': "Flask Book API"
}
)
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)

if __name__ == '__main__':
app.run(debug=True)

Testing the Swagger UI

  1. Run your Flask application:
bash
python app.py
  1. Open a web browser and navigate to http://127.0.0.1:5000/api/docs

You should see the Swagger UI interface displaying your API documentation. From here, you can:

  • View all available endpoints
  • Understand the expected parameters and responses
  • Test endpoints directly through the interface
  • See example responses

Best Practices for Swagger Documentation

  1. Be Comprehensive: Document all endpoints, parameters, and possible responses
  2. Include Examples: Provide example requests and responses
  3. Keep It Updated: Always update your documentation when you change your API
  4. Use Descriptive Names: Use clear, descriptive names for endpoints and models
  5. Group Related Endpoints: Organize your API documentation by grouping related endpoints
  6. Document Error Responses: Include all possible error responses for each endpoint

Real-World Example: Building a Complete Book Management API

Let's expand our example to include more operations like updating and deleting books:

python
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from marshmallow import Schema, fields
from flask_swagger_ui import get_swaggerui_blueprint

app = Flask(__name__)
api = Api(app)

# Create an APISpec
spec = APISpec(
title="Book Management API",
version="1.0.0",
openapi_version="3.0.2",
plugins=[FlaskPlugin(), MarshmallowPlugin()]
)

# Define schemas
class BookSchema(Schema):
id = fields.Int(dump_only=True)
title = fields.Str(required=True, description="Book title")
author = fields.Str(required=True, description="Author name")
published_year = fields.Int(description="Year the book was published")
genre = fields.Str(description="Book genre")

# Register schemas with spec
spec.components.schema("Book", schema=BookSchema)

# Sample data
books = [
{"id": 1, "title": "Flask Web Development", "author": "Miguel Grinberg", "published_year": 2018, "genre": "Programming"},
{"id": 2, "title": "Python Crash Course", "author": "Eric Matthes", "published_year": 2019, "genre": "Programming"}
]

# Resources
class BookResource(Resource):
def get(self, book_id):
"""Get a book by ID
---
parameters:
- in: path
name: book_id
required: true
schema:
type: integer
responses:
200:
description: Return a book
content:
application/json:
schema: BookSchema
404:
description: Book not found
"""
book = next((book for book in books if book['id'] == book_id), None)
if book:
return book
return {"error": "Book not found"}, 404

def put(self, book_id):
"""Update a book
---
parameters:
- in: path
name: book_id
required: true
schema:
type: integer
requestBody:
content:
application/json:
schema: BookSchema
responses:
200:
description: Book updated
content:
application/json:
schema: BookSchema
404:
description: Book not found
"""
book = next((book for book in books if book['id'] == book_id), None)
if not book:
return {"error": "Book not found"}, 404

if not request.json:
return {"error": "Invalid data"}, 400

book.update({
'title': request.json.get('title', book['title']),
'author': request.json.get('author', book['author']),
'published_year': request.json.get('published_year', book.get('published_year')),
'genre': request.json.get('genre', book.get('genre'))
})
return book

def delete(self, book_id):
"""Delete a book
---
parameters:
- in: path
name: book_id
required: true
schema:
type: integer
responses:
204:
description: Book deleted
404:
description: Book not found
"""
global books
book = next((book for book in books if book['id'] == book_id), None)
if not book:
return {"error": "Book not found"}, 404

books = [b for b in books if b['id'] != book_id]
return "", 204

class BookListResource(Resource):
def get(self):
"""Get all books
---
parameters:
- in: query
name: genre
schema:
type: string
description: Filter books by genre
responses:
200:
description: Return a list of books
content:
application/json:
schema:
type: array
items: BookSchema
"""
genre = request.args.get('genre')
if genre:
filtered_books = [book for book in books if book.get('genre') == genre]
return filtered_books
return books

def post(self):
"""Add a new book
---
requestBody:
content:
application/json:
schema: BookSchema
responses:
201:
description: Book created
content:
application/json:
schema: BookSchema
400:
description: Invalid input
"""
if not request.json or not request.json.get('title') or not request.json.get('author'):
return {"error": "Title and author are required"}, 400

book = {
'id': max(b['id'] for b in books) + 1 if books else 1,
'title': request.json['title'],
'author': request.json['author'],
'published_year': request.json.get('published_year'),
'genre': request.json.get('genre')
}
books.append(book)
return book, 201

# Register resources
api.add_resource(BookListResource, '/api/books')
api.add_resource(BookResource, '/api/books/<int:book_id>')

# Register paths with spec
with app.test_request_context():
spec.path(view=BookListResource, app=app)
spec.path(view=BookResource, app=app)

# Serve OpenAPI specification
@app.route('/api/swagger.json')
def create_swagger_spec():
return jsonify(spec.to_dict())

# Configure Swagger UI
SWAGGER_URL = '/api/docs'
API_URL = '/api/swagger.json'

swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL,
API_URL,
config={
'app_name': "Book Management API"
}
)
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)

if __name__ == '__main__':
app.run(debug=True)

Summary

In this tutorial, you've learned how to:

  1. Integrate Swagger UI with your Flask application
  2. Document your API manually using a Swagger JSON file
  3. Automate API documentation using apispec and docstrings
  4. Build a complete RESTful API with comprehensive documentation

Swagger integration significantly improves the developer experience for those consuming your API. It provides clear, interactive documentation that allows users to understand and test your API directly from their browser.

Additional Resources

Exercises

  1. Add authentication to your API and document it in the Swagger specification
  2. Create a more complex model with relationships and document it
  3. Add query parameters for filtering and sorting to your GET endpoints
  4. Implement validation for request inputs using Marshmallow schemas
  5. Create a front-end application that consumes your API using the Swagger-generated client


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)