So, you have your API working like a charm. But what if someone else wants to use it? That's where documentation comes in. This blog will guide you through the process of creating Swagger documentation to make RESTful APIs in Flask accessible to fellow developers.
We will create an API for a calculator that computes the combination and permutation for n
items chosen from a set of k
. Here are the two essential endpoints we'll start with:
This endpoint will calculate the number of ways to choose k
items from a set of n
items without regard to the order, i.e. combination:
GET /combinations?n=5&k=3
This endpoint will calculate the number of ways to arrange k
items from a set of n
items with regard to the order, i.e. permutation:
GET /permutations?n=5&k=3
Intrigued? Let's dive into the code and build our endpoints.
Creating Endpoints:
We will start by defining a few helper functions for computing the combination and permutations. Keep in mind that n
is the total # items to choose from, and k
is the # items chosen. These functions also include input validation to ensure that n
and k
are non-negative integers, and n
is greater than or equal to k
.
import math
def calculate_combinations_logic(n, k):
if n < 0 or k < 0 or n < k:
raise ValueError("Invalid input")
combinations = math.factorial(n) // (math.factorial(k) * math.factorial(n - k))
return combinations
def calculate_permutations_logic(n, k):
if n < 0 or k < 0 or n < k:
raise ValueError("Invalid input")
permutations = math.factorial(n) // math.factorial(n - k)
return permutations
a. Combinations:
from flask import Flask, request
app = Flask(__name__)
@app.route('/combinations', methods=['GET'])
def calculate_combinations():
n = int(request.args.get('n'))
k = int(request.args.get('k'))
result = calculate_combinations_logic(n, k)
return f"Combinations of {n} choose {k}: {result}"
b. Permutations:
@app.route('/permutations', methods=['GET'])
def calculate_permutations():
n = int(request.args.get('n'))
k = int(request.args.get('k'))
result = calculate_permutations_logic(n, k)
return f"Permutations of {n} choose {k}: {result}"
But wait, how do others know how to use them? That's where Swagger documentation comes into play.
Swagger Docs:
Now we will integrate Swagger into our API. Let’s begin by installing the flask-swagger package:
pip install flask-swagger,flask_swagger_ui
Also, let us download the latest release from the swagger-ui GitHub. Un-tar this, and move the dist
directory to a separate directory named swaggerui
and create another new directory named static
. Move swaggerui
to static
.
Your folder structure should look something like this:
(base) kopalgarg@kgarg swagger-documentation % ls -lah
total 9248
drwxr-xr-x@ 5 kopalgarg staff 160B Nov 28 18:17 .
drwxr-xr-x@ 94 kopalgarg staff 2.9K Nov 28 18:10 ..
-rw-r--r-- 1 kopalgarg staff 2.5K Nov 28 18:32 api.py
drwxr-xr-x 3 kopalgarg staff 96B Nov 28 18:24 static
-rw-r--r-- 1 kopalgarg staff 3.8M Nov 28 18:15 v5.10.3.tar.gz
(base) kopalgarg@kgarg swagger-documentation % ls -lah static/swaggerui
total 20248
drwxr-xr-x 20 kopalgarg staff 640B Nov 22 01:04 .
drwxr-xr-x 3 kopalgarg staff 96B Nov 28 18:24 ..
-rw-r--r-- 1 kopalgarg staff 665B Nov 22 01:04 favicon-16x16.png
-rw-r--r-- 1 kopalgarg staff 628B Nov 22 01:04 favicon-32x32.png
-rw-r--r-- 1 kopalgarg staff 202B Nov 22 01:04 index.css
-rw-r--r-- 1 kopalgarg staff 834B Nov 28 18:24 index.html
-rw-r--r-- 1 kopalgarg staff 2.7K Nov 22 01:04 oauth2-redirect.html
-rw-r--r-- 1 kopalgarg staff 501B Nov 28 18:23 swagger-initializer.js
-rw-r--r-- 1 kopalgarg staff 1.3M Nov 22 01:04 swagger-ui-bundle.js
-rw-r--r-- 1 kopalgarg staff 1.8M Nov 22 01:04 swagger-ui-bundle.js.map
-rw-r--r-- 1 kopalgarg staff 450K Nov 22 01:04 swagger-ui-es-bundle-core.js
Modify static/index.html
with the correct paths:
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="static/swaggerui/swagger-ui.css" />
<script src="static/swaggerui/swagger-ui-bundle.js" charset="UTF-8"></script>
<script src="static/swaggerui/swagger-ui-standalone-preset.js" charset="UTF-8"></script>
<script src="static/swaggerui/swagger-initializer.js" charset="UTF-8"></script>
</head>
<body>
<div id="swagger-ui"></div>
<script src="static/swaggerui/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="static/swaggerui/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="static/swaggerui/swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>
Modify static/swagger-initializer.js
with the correct paths:
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
url: "/spec",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
});
//</editor-fold>
};
Once we have the set-up out of the way, we will modify our Flask API code to use flask-swagger
for documentation:
from flask import Flask, request, jsonify
from flask_swagger import swagger
import math
def calculate_combinations_logic(n, k):
if n < 0 or k < 0 or n < k:
raise ValueError("Invalid input")
combinations = math.factorial(n) // (math.factorial(k) * math.factorial(n - k))
return combinations
def calculate_permutations_logic(n, k):
if n < 0 or k < 0 or n < k:
raise ValueError("Invalid input")
permutations = math.factorial(n) // math.factorial(n - k)
return permutations
app = Flask(__name__)
@app.route("/spec")
def spec():
swag = swagger(app)
swag['info']['title'] = "Combination and Permutation"
swag['info']['version'] = "1.0.0"
return jsonify(swag)
@app.route('/api-docs')
def swagger_ui():
return app.send_static_file('swaggerui/index.html')
@app.route('/combinations', methods=['GET'])
def calculate_combinations():
"""
Calculate the number of combinations (n choose k).
This endpoint calculates the number of ways to choose k items from a set of n items without regard to the order (combinations).
---
parameters:
- name: n
in: query
type: integer
required: true
description: Total number of items to choose from.
- name: k
in: query
type: integer
required: true
description: Number of items to choose.
responses:
200:
description: The number of combinations.
"""
n = int(request.args.get('n'))
k = int(request.args.get('k'))
result = calculate_combinations_logic(n, k)
return f"Combinations of {n} choose {k}: {result}"
@app.route('/permutations', methods=['GET'])
def calculate_permutations():
"""
Calculate the number of permutations (n P k).
This endpoint calculates the number of ways to arrange k items from a set of n items with regard to the order (permutations).
---
parameters:
- name: n
in: query
type: integer
required: true
description: Total number of items to arrange from.
- name: k
in: query
type: integer
required: true
description: Number of items to arrange.
responses:
200:
description: The number of permutations.
"""
n = int(request.args.get('n'))
k = int(request.args.get('k'))
result = calculate_permutations_logic(n, k)
return f"Permutations of {n} choose {k}: {result}"
if __name__ == '__main__':
app.run()
Testing it out
Assuming you saved your API code in Python file named api.py
, if you run python3 api.py
, you should see something like this:
If you open up http://127.0.0.1:5000/
, you should see the raw JSON for the specs on http://127.0.0.1:5000/spec
:
and the Swagger documentation on http://127.0.0.1:5000/api-docs
:
Now, if you wanted to test the API, you could do so directly from the documentation Swagger page, like so:
That’s all for now! Let me know if you have any thoughts, and feel free to subscribe to my blog if you found this helpful. You can also find me on LinkedIn and GitHub.