FLASK-RESTPLUS: SWAGGER, TOKEN AUTHENTICATION, BLUEPRINTS

shorya sharma
7 min readJan 4, 2021

Hi, Let me ask you a question, have you ever wondered what is the correct way to make an API in Flask? Though there are different ways of making APIs in Flask, you may use the normal single script for all the APIs, you may use flask restful framework or you may use flask restplus framework.

Well, my question remains the same, what is the correct way?

Correctness differs from person to person and on the persons' experiences, the correct way of writing a flask API or any API for me is when it is accepted as per industry norms and provides an endpoint where a QA tester and the Frontend developer can test the API without using any other software namely postman or doesn’t have to get into the nitty-gritty of the API code.

So what is the easiest way to make a correct API which tick marks all the above-mentioned criteria, by structuring and layering the API code using a framework and implementing Swagger UI for documentation.

I suppose you know what is an API? to be precise, API is the abbreviation for Application Programming Interface, which is a software product middle person that permits two applications to converse with one another.

It has 4 methods namely,

  1. GET: Retrieve information about the REST API resource
  2. POST: Create a REST API resource
  3. PUT: Update a REST API resource
  4. DELETE: Delete a REST API resource or related component

In this blog post, I will help my readers to build APIs using all 4 mentioned methods via a very simple example, and by the end of the post, the readers will be able to build APIs with token authentication and swagger UI.

We will use flask restplus as the framework for making the API and will use blueprint architecture along with waitress production server.

PREREQUISITES

  1. Python installed
  2. Python virtual environment preferred
  3. Text editor of choice ( pycharm, VS code, etc)

Now follow the steps one by one and you will be able to create the API

STEPS:

  1. Activate the Python Virtual Environment and run the following commands
pip install -U Flaskpip install flask-restpluspip install waitress

2. Create a folder that will be your project name, I used the name tutorial as my project name.

3. Inside your project folder create 3 folders with the name blueprints, data, and namespaces.

4. Inside your Project add a python file with the name __init__.py

now at this point, your project structure looks like this :

5. Inside your __init__.py file write the following code:

from flask import *


def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '9OLWxND4o83j4K4iuopO'
return app

6. Now inside your namespaces folder add 4 files, with names __init__.py, namespacesA.py, namespacesB.py, and util.py. The __init__.py file should be empty.

Namespaces appear to be intended for organizing REST endpoints within a given API.

7. Inside your NamespacesA.py file write the following code.

from flask_restplus import Resource, fields, Namespace
from werkzeug.exceptions import BadRequest
from tutorial.namespaces.util import token_required

authorizations = {
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'X-API-KEY'
}
}

api = Namespace('testing', authorizations=authorizations)


sum_of_integer = api.model('sum', {
"number1": fields.Integer('1'),
"number2": fields.Integer('2')
})

hello_name = api.model('hello', {
"name": fields.String('Shorya')
})


@api.route('/hello')
class HelloWorld(Resource):
@api.doc(security='apikey')
@token_required
def get(self):
return {'hello': 'world'}

@api.expect(hello_name)
@token_required
def post(self):
data = api.payload
name = data['name']
return {'hello': name}


@api.route('/sum')
class SumOfNumbers(Resource):
@api.expect(sum_of_integer)
@token_required
def post(self):
try:
data = api.payload
number1 = data['number1']
number2 = data['number2']
sum = number1 + number2
return {'sum': sum}
except:
raise BadRequest()

8. Inside your NamespacesB.py file write the following code

from flask_restplus import Resource, fields, Namespace
from werkzeug.exceptions import BadRequest
import json
from tutorial.namespaces.util import token_required


authorizations = {
'apikey': {
'type': 'apiKey',
'in': 'header',
'name': 'X-API-KEY'
}
}

api = Namespace('tasks', authorizations=authorizations)

add_tasks = api.model('tasks', {
"title": fields.String('complete test cases'),
"description": fields.String('tests cases for global project'),
"done": fields.Boolean(False)
})


@api.route('/gettasks')
class Tasks(Resource):
@api.doc(security='apikey')
@token_required
def get(self):
with open('tutorial/data/tasks.json') as f:
tasksList = json.load(f)
return tasksList


@api.route('/addTasks')
class AddTasks(Resource):
@api.doc(security='apikey')
@api.expect(add_tasks)
@token_required
def post(self):
with open('tutorial/data/tasks.json','r') as f:
tasksList = json.load(f)
data = api.payload
title = data.get('title')
description = data.get('description')
done = data.get('done')
task = {
'id': tasksList['tasks'][-1]['id'] + 1,
'title': title,
'description': description,
'done':done
}
tasksList['tasks'].append(task)
with open('tutorial/data/tasks.json', 'w') as fp:
json.dump(tasksList, fp)

return {'result': 'Task Added', 'success': True}


@api.route('/<int:task_id>')
class EditTasks(Resource):
@api.doc(security='apikey')
@token_required
def delete(self, task_id):
with open('tutorial/data/tasks.json','r') as f:
tasksList = json.load(f)
task = [task for task in tasksList['tasks'] if task['id'] == task_id]
if len(task) == 0:
raise BadRequest()
tasksList['tasks'].remove(task[0])
with open('tutorial/data/tasks.json', 'w') as fp:
json.dump(tasksList, fp)
return {'result': 'Task Deleted', 'success': True}

@api.expect(add_tasks)
@api.doc(security='apikey')
@token_required
def put(self, task_id):
with open('tutorial/data/tasks.json','r') as f:
tasksList = json.load(f)
if len(tasksList) == 0:
return {"message": "no tasks created"}
if not api.payload:
raise BadRequest()
if 'title' in api.payload and type(api.payload['title']) != str :
raise BadRequest()
if 'description' in api.payload and type(api.payload['description']) is not str:
raise BadRequest()
if 'done' in api.payload and type(api.payload['done']) is not bool:
raise BadRequest()
for task in tasksList['tasks']:
if task['id'] == task_id:
task['title'] = api.payload.get('title')
task['description'] = api.payload.get('description')
task['done'] = api.payload.get('done')
with open('tutorial/data/tasks.json', 'w') as fp:
json.dump(tasksList, fp)
return {'result': 'Task updated', 'success': True}
else:
continue
return {'message': "no such record found"}

Now If you noticed we created 2 different namespaces, namespacesA is to demonstrate a basic helloWorld API with GET & POST and to sum 2 numbers. whereas namespacesB is to demonstrate a complete tasks API, which performs GET , POST , PUT and DELETE where our task is to read a task, add a new task, and update/delete a previous task.

9. In the namespaces code I used a decorator ‘@token_required’, the code for which will go into the util.py file inside the namespaces folder

Inside util.py write the following lines of code:

from flask import request
from functools import wraps


def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = None

if 'X-API-KEY' in request.headers:
token = request.headers.get('X-API-KEY')

if not token:
return {'message' : 'Token is missing'}, 401

if token != 'myToken':
return {'message': 'your token is wrong'}, 401

return f(*args, **kwargs)
return decorated

10. Now inside the data folder add a JSON file with the name tasks.json

{"tasks": [{"description": "Milk, Cheese, Pizza, Fruit, Tylenol", "done": false, "id": 1, "title": "Buy groceries"}, {"description": "Need to find a good Python tutorial on the web", "done": false, "id": 2, "title": "Learn Python"}]}

11. Now inside the blueprints folder add 2 files with the name, __init__.py, and main.py

12. Inside the main.py file we need to register our namespaces, Blueprints, in this context, appear to be intended for allowing multiple APIs to be mixed and matched with other APIs or non-REST routes on a Flask App. It also helps you to do version control of your APIs.

Write the following code inside the main.py file:

from flask import Blueprint
from flask_restplus import Api
from tutorial.namespaces.namespacesA import api as ns1
from tutorial.namespaces.namespacesB import api as ns2


blueprint = Blueprint('main', __name__)


main = Api(blueprint)

main.add_namespace(ns1)
main.add_namespace(ns2)

13. Wondering how your API knows about the blueprint? We will register the blueprint to the flask API inside the __init__.py inside the tutorial folder.

Update the __init__.py file with the following code

import werkzeug
werkzeug.cached_property = werkzeug.utils.cached_property
from flask import *
from flask_cors import CORS

def create_app():
app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = '9OLWxND4o83j4K4iuopO'
from tutorial.blueprints.main import blueprint as api1
app.register_blueprint(api1)
return app

14. Now we are ready to run our API :

In the terminal where you have activated the virtual environment, run the following command outside the project directory, for example, my project (tutorial) is having the path D:\tutorial, so I will run the command at location D:\

D:\>waitress-serve --port=5000 --call tutorial:create_app

reserachenv is the name of my virtual environment, you can have any.

The swagger is ready, type localhost:5000 on your web browser

NOTE: We added two namespaces, where namespacesA was having the name testing and the namespacesB was having name tasks. The APIs inside the respective namespaces our clubbed together, thus providing structure and layering to the code.

Also notice, the lock icon in front of the APIs indicates that the API needs to be authorized before trying. not all the APIs have the lock icon which means that API is not required to be authorized.

you can simply add the authorization to the APIs by adding the decorator @api.doc(security=’apikey’) and @token_required at the top of the API method.

Trying API :

  1. Authorize the API, we set the token as myToken in the util.py so click on the authorize button at the top right corner:

Try the GET API in /testing/hello

2. Test the POST /tasks/addTasks/ Api

Click on Execute and see the response

3. Now try the GET /tasks/gettasks to see our added tasks:

You may try the other APIs on your own now.

GITHUB LINK: https://github.com/shorya1996/tutorial_restplus

--

--

shorya sharma

Assistant Manager at Bank Of America | Ex-Data Engineer at IBM | Ex - Software Engineer at Compunnel inc. | Python | Data Science