Swagger UI to your Python Flask API

Adding a visual sensation to your ML Flask application

Dinithi Dissanayake
ADL AI & Analytics Corner
6 min readJul 10, 2023

--

Flask is the go-to library for API development in Python, mostly because of its light weight and already implemented development server and debugging tool. Swagger in the other hand is a set of tools helping API developers tackle its lifecycle. Among its suite of tools, Swagger-UI gives us an interactive web UI to test the created API, similar to what we do using Postman. Some of the API gateways use the capabilities of the swagger UI to give a visual sensation of the created API to the users.

Unlike spring boot swagger UI generators there is no easy way of generating swaggers for flask-based python APIs. I have put together the use of the library flasgger to build an interactive UI for your flask application.

Let’s first create a simple flask API.

Flask API for Wine Quality Prediction

I have trained a simple regression model which predicts the quality of a wine when given the following features,

  1. fixed acidity
  2. volatile acidity
  3. citric acid
  4. residual sugar
  5. chlorides
  6. free sulfur dioxide
  7. total sulfur dioxide
  8. density
  9. pH
  10. sulphates
  11. alcohol

The dataset was obtained from https://www.kaggle.com/datasets/uciml/red-wine-quality-cortez-et-al-2009.

Let’s create a simple API to input these parameters and receive the wine quality as the output.

from flask import Flask, render_template, Response, jsonify, request
import flask
from flask import Flask, request, jsonify
from flask import Blueprint
from flasgger import Swagger, LazyString, LazyJSONEncoder
from flasgger import swag_from
import joblib
import sklearn
import numpy as np
import json

app = Flask(__name__)
app.json_encoder = LazyJSONEncoder

model= joblib.load('wqmodel.sav')

#################################################################################################################################################################################
@app.route("/predict",methods = ["POST"])
def predict():
data = {} # dictionary to store result
data['code'] = 1
data_n = request.get_json()
if request.method == "POST":
try:
array= np.array(data_n["array"])

#for invalid data keys
except KeyError:
return jsonify({"responseCode": "2","responseDesc": "Invalid data Key"}), 400

#if the input is empty
if array is None:
print("Array Input Failed..")
data['response'] = 'Input Reading Error: Input is Empty'
return jsonify({"responseCode": "3","responseDesc": "Input Reading Error: Input is Empty"}), 400

#if the input array is not the required size
elif len(array)!=11:
print("Array Input InComplete..")
data['response'] = 'Input Reading Error: Input is Incomplete'
return jsonify({"responseCode": "3","responseDesc": "Input Reading Error: Input is Incomplete"}), 400

else:
array=array.reshape(1,11)
#prediction
prediction = model.predict(array)
data['code'] = 0
data['response'] = 'Model prediction PASSED'
data['prediction'] = prediction
response={"responseCode": "0","responseDesc": "SUCCESS","Wine Quality": prediction[0]}
return jsonify(response),200

else:
return jsonify({"responseCode": "1","responseDesc": "Method Not Allowed"}), 405

if __name__ == '__main__':
print(("* Loading model and Flask starting server..."
"please wait until server has fully started"))
app.run(host='0.0.0.0', threaded=True, port=8085 , debug=False)

I have trained a simple Elastic Net model to predict the wine quality and when this app receives the following POST request it will give the wine quality prediction as depicted below.

curl --location 'http://localhost:5000/predict' \
--header 'Content-Type: application/json' \
--data '{
"array": [7, 0.27, 0.36, 20.7, 0.045, 45, 170, 1.001, 3, 0.45, 6]
}'
{
"Wine Quality": 4.939022628389594,
"responseCode": "0",
"responseDesc": "SUCCESS"
}

You can easily check the functioning with the aid of any API platform like Postman. Now I will show how you can create a swagger UI for inbuilt testing of this flask API without the need of any external platform.

Building the swagger UI

When using flasgger we should add the API specification as yaml either in the same python script or using an externel yaml file. I have used a separate yaml file since it is easy to debug and does not clutter the API file.

The separate yaml file(located at ../docs/predict.yaml) is as follows.

description: "This API will be used to predict the class of the uploaded image"
tags:
- name: Predict the Wine Quality
consumes:
- application/json
produces:
- application/json
parameters:
- description: "The body should contain the array containing the 11 input parameters in the order of:
fixed acidity, volatile acidity, citric acid, residual sugar, chlorides, free sulfur dioxide, total sulfur dioxide,
density, ph, sulphates, alcohol"
in: "body"
name: "body"
schema:
title: predict
type: object
properties:
array:
type: array
description: Input Array
example:
array : [7, 0.27, 0.36, 20.7, 0.045, 45, 170, 1.001, 3, 0.45, 6]
required: true
responses:
"200":
description: Successful response
schema:
title: Valid Prediction Response
type: object
example:
Wine Quality: 4.939022628389594
responseCode: 0
responseDesc: SUCCESS
"400":
description: Bad request
schema:
title: Invalid data Key
type: object
example:
responseCode: 2
responseDesc: Invalid data Key
"401":
description: Bad request
schema:
title: Input is not an array of size 11
type: object
example:
responseCode: 3
responseDesc: "Input Reading Error: Input is Incomplete"

"405":
description: Method Not Allowed
schema:
title: Method Not Allowed
type: object
example:
responseCode: 1
responseDesc: Method Not Allowed

You should create a yaml file for each of the endpoints(routes) in your API, including what the endpoint consumes and produces. You can include examples so that the swagger UI will give you the chance to try it out. More descriptive you are in creating the yaml file, more appealing and descriptive your UI will be.

After creating your yaml file your python script should include additional swagger configurations. I will give you a basic example, so that you can edit the information to suit your need.

from flask import Flask, render_template, Response, jsonify, request
import flask
from flask import Flask, request, jsonify
from flask import Blueprint
from flasgger import Swagger, LazyString, LazyJSONEncoder
from flasgger import swag_from
import joblib
import sklearn
import numpy as np
import json

app = Flask(__name__)
app.json_encoder = LazyJSONEncoder

swagger_template ={
"swagger": "2.0",
"info": {
"title": "Wine Quality Prediction",
"description": "API Documentation for Wine Quality Prediction",
"contact": {
"name": "Admin",
"email": "dinithi@axiatadigitallabs.com",
"url": "http://www.axiatadigitallabs.com",
},
"termsOfService": "Terms of services",
"version": "1.0",
"host":"Wine_Quality_Prediction",
"basePath":"http://localhost:5000",
"license":{
"name":"License of API",
"url":"API license URL"
}
},
"schemes": [
"http",
"https"
],
}

swagger_config = {
"headers": [
('Access-Control-Allow-Origin', '*'),
('Access-Control-Allow-Methods', "GET, POST"),
],
"specs": [
{
"endpoint": 'Wine_Quality_Prediction',
"route": '/Wine_Quality_Prediction.json',
"rule_filter": lambda rule: True,
"model_filter": lambda tag: True,
}
],
"static_url_path": "/flasgger_static",
"swagger_ui": True,
"specs_route": "/apidocs/",

}
swagger = Swagger(app, template=swagger_template,config=swagger_config)

model= joblib.load('wqmodel.sav')

#################################################################################################################################################################################
@swag_from("docs/predict.yaml" )
@app.route("/predict",methods = ["POST"])
def predict():
data = {} # dictionary to store result
data['code'] = 1
data_n = request.get_json()
if request.method == "POST":
try:
array= np.array(data_n["array"])

except KeyError:
return jsonify({"responseCode": "2","responseDesc": "Invalid data Key"}), 400

if len(array)!=11:
print("Array Input InComplete..")
data['response'] = 'Input Reading Error: Input is Incomplete'
return jsonify({"responseCode": "3","responseDesc": "Input Reading Error: Input is Incomplete"}), 401

else:
array=array.reshape(1,11)
prediction = model.predict(array)
data['code'] = 0
data['response'] = 'Model prediction PASSED'
data['prediction'] = prediction
response={"responseCode": "0","responseDesc": "SUCCESS","Wine Quality": prediction[0]}
return jsonify(response),200

else:
return jsonify({"responseCode": "1","responseDesc": "Method Not Allowed"}), 405

if __name__ == '__main__':
print(("* Loading model and Flask starting server..."
"please wait until server has fully started"))
app.run(host='0.0.0.0', threaded=True, port=8085 , debug=False)

You can access your swagger UI at you “base path” + “specs route” mentioned in the script. My one would be, http://localhost:5000/apidocs/. The following figure depicts the UI you can visit using the above link.

Swagger UI

When you click on top of the /predict widget it collapses and shows your specifications to the extent you mentioned in the yaml file.

Swagger UI Expanded

Let’s try out the predict endpoint!

Once you click the try it out button you will get the example in an editable area, and you can edit and execute it.

Trying out the predict API

The UI outputs the wine quality successfully.

You can receive the documentation for this API at http://localhost:5000/Wine_Quality_Prediction.json , same as we specified in the swagger template.

It is possible to include security features, auth tokens, and any type of input and output data differing to the ones we used here. Hope this helps you to figure out an easy way to document and build a UI to test your python flask API.

Disclaimer: ADL is not responsible for any damage caused byanyofthearticlesto internal or external parties.

--

--

ADL AI & Analytics Corner
ADL AI & Analytics Corner

Published in ADL AI & Analytics Corner

Meet the AI & Analytics team at Axiata Digital Labs as they bring a series of blogs to keep you updated with cutting-edge trends, breakthroughs, and advancements in the ever-evolving fields of artificial intelligence and analytics.

Dinithi Dissanayake
Dinithi Dissanayake

Written by Dinithi Dissanayake

ML Enthusiast: Attempting to share everything I learn!

No responses yet