Building my first Flask API
Module 2 :

Welcome back,
This is our second module of building our flask API. In our previous module we built our first flask application using Python and developed our route decorator for GET method.
In this module, we will discuss about the following:
2.POSTING DATA
2.1 Adding a POST route
2.2 Getting the Request body sent by the client
2.3 Sending POST request to our Flask API using POSTMAN app
2.4 Sanitizing Data sent in POST request
2.5 Testing how well we sanitized
2.6 Adding New book to our Store
2.7 Mistake to be aware
2.8 Setting Status codes and Response
2.9 Setting location Headers
2.10 Handling invalid POST requests
By the end of this module, you will be able to do POST request with a clean Sanitized Data which returns a meaningful Status Code and Response.
Lets get inside the module..
2.POSTING DATA
2.1 Adding a POST route
#adding POST request
@app.route(‘/books’, methods=[‘POST’]) #POST request will be fired
def add_book(): #allows client to add a new entry
pass #empty placeholder(we will define later)
Runnable Code Snippet_2.1- Adding a POST route:
# 2.1 Adding a POST route
from flask import Flask, jsonify ,request #request library
#app instance
app = Flask(__name__)
print(__name__)
#creating List with 2 dictionaries
books = [
{
'name' : 'Book1',
'price' : 10,
'isbn' : 987654
},
{
'name' : 'Book2',
'price' : 20,
'isbn' : 123456
}
]
#route decorators
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Menu': books})
#adding POST request
@app.route('/books', methods=['POST']) #POST request will be fired
def add_book(): #allows client to add a new entry
pass #empty placeholder(we will define later)
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={} #empty dictionary to hold ret_val
for book in books:
if book["isbn"] == isbn:
return_value = {
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
Save and Run from CLI, to check the code is working without any error. We are yet to define the method.
2.2 Getting the Request body sent by the client
In POST request, details of the new entry must be given by the Client in their RESPONSE BODY as an OBJECT, the request must be in the following format :
{
‘name’: ‘sample’,
‘price’: 56,
‘isbn’:8965565
}
This POST request body is sent as a JSON object by the Client.
Defining the POST method, as folows:
def add_book(): #allows client to add a new entry
return jsonify(request.get_json()) #get the JSON request body that our client sent
Here, in order to test the req body sent to us, we return jsonify
Runnable Code Snippet_2.2- Getting the Request body sent by the Client:
# 2.2 Getting the Request body sent by the client
from flask import Flask, jsonify ,request #request library
#instance
app = Flask(__name__)
print(__name__)
#creating List with 2 dictionaries
books = [
{
'name' : 'Book1',
'price' : 10,
'isbn' : 987654
},
{
'name' : 'Book2',
'price' : 20,
'isbn' : 123456
}
]
#route decorators
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Menu': books})
#in POST request,details of the new entry must be given by the Client
#in their RESPONSE BODY as an OBJECT
#the request looks should something like below
"""
{
'name': 'sample',
'price': 56,
'isbn':8965565
}
"""
#Client send this as a JSON object
#adding POST request
@app.route('/books', methods=['POST']) #POST request will be fired
def add_book(): #allows client to add a new entry
return jsonify(request.get_json()) #get the JSON request body that our client sent
#in order to test the req body sent to us, we return jsonify
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={} #empty dictionary to hold ret_val
for book in books:
if book["isbn"] == isbn:
return_value = {
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
2.3 Sending POST request to our Flask API using POSTMAN app
Postman download link is provided here:https://www.getpostman.com/downloads/
Route decorator and methods are same as above, now we are going to save and run the app.
Runnable Code Snippet_2.3- Sending a POST request:
# 2.3 Sending POST request to our Flask API using POSTMAN app
from flask import Flask, jsonify ,request
#app instance
app = Flask(__name__)
print(__name__)
#creating List with 2 dictionaries
books = [
{
'name' : 'Book1',
'price' : 10,
'isbn' : 987654
},
{
'name' : 'Book2',
'price' : 20,
'isbn' : 123456
}
]
#route decorators
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Menu': books})
#adding POST request
@app.route('/books', methods=['POST']) #POST request will be fired
def add_book(): #allows client to add a new entry
return jsonify(request.get_json())
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={} #empty dictionary to hold ret_val
for book in books:
if book["isbn"] == isbn:
return_value = {
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
Follow the given steps to fire a POST request using Postman
TO USE POSTMAN:
step1: set URL http://127.0.0.1:5000/books
step2: set request POST
step3: set Headers->KEY->Content-Type as application/json
step4: set Body->
{
“name”: “New Book1”,
“price”: 20,
“isbn”: 23434
}
step5: Hit Send button at the Right Top corner
step6: Check the Response window
->Headers->Content-Type: application/json
(i.e., we have got the req data our client sent) and status: 200 OK
Behind the Screen
Postman send a POST Request to our (server)books route and then our add_book() method ran, which got the request JSON, jsonified it, set the right content Headers back to us, and then sent that Response back (to postman)
2.4 Sanitizing Data sent in POST request
We may get some garbage data passed along with the request body. So it’s a good practice to Sanitize the data received from Client, as follows
we need to check if “keyName” is in the dictionaryObject
we define a valid object in the method as follows,
def validBookObject(bookObject):
if (“name” in bookObject and “price” in bookObject and “isbn” in bookObject):
return True
else:
return False
Runnable Code Snippet_2.4- Sanitizing Data sent in POST request:
# 2.4 Sanitizing Data sent in POST request
from flask import Flask, jsonify,request
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#Sanitize the data received from Client
#if "keyName" in dictionaryObject
def validBookObject(bookObject):
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
return jsonify(request.get_json())
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
2.5 Testing how well we sanitized Invalid Request Bodies
We create a separate file called ‘DataSanitizer_TestCase.py’ to check how well we sanitized invalid request bodies being sent to us
Runnable Code Snippet_2.5- Testing how well we sanitized Invalid Request bodies:
# 2.5 Testing how well we sanitized Invalid Request Bodies
#DataSanitizer_TestCase.py
def validBookObject(bookObject):
#if "keyName" in dictionaryObject
if ("name" in bookObject
and "price" in bookObject
and "isbn" in bookObject):
return True
else:
return False
valid_object = {
'name' : 'sample',
'price' : 4.5,
'isbn' : 963
}
missing_name = {
'price' : 4.5,
'isbn' : 963
}
missing_price = {
'name' : 'sample',
'isbn' : 963
}
missing_isbn = {
'name' : 'sample',
'price' : 4.5
}
empty_dictionary = {}
CLI Output
rs@rs-pc:~/RSflaskAPI$ python
Python 3.6.8 (default)
>>> from DataSanitizer_TestCase import *
>>> validBookObject(valid_object)
True
>>> validBookObject(missing_name)
False
>>> validBookObject(missing_price)
False
>>> validBookObject(missing_isbn)
False
>>> validBookObject(empty_dictionary)
False
>>> validBookObject(valid_object)
True
>>> exit()
rs@rs-pc:~/RSflaskAPI$
2.6 Adding New book to our Store using POST
#Adding a POST route
#POST/books
@app.route(‘/books’, methods=[‘POST’])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
books.insert(0, request_data)#append bookObject we received from client,into BookList
return “True”
else:
return “False”
Runnable Code Snippet_2.6- Adding new book to our store using POST:
# 2.6 Adding New book to our Store using POST
from flask import Flask, jsonify,request
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body format
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#data sanitizer
def validBookObject(bookObject):
#if entry already exists
for i in books:
if i["isbn"] == bookObject["isbn"]:
return False
else:
#if "keyword" in dictionary
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
books.insert(0, request_data)#append bookObject we received from client,into BookList
return "True"
else:
return "False"
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
Using CLI:
Save and run the app in CLI
$ python RSapp.py
Steps to be followed and its expected output are given below:
Using Postman
step1:
GET request fired at http://127.0.0.1:5000/books
output:
list of all books in JSON
step2:
POST request fired at http://127.0.0.1:5000/books
with body
{
“name”: “New Book”,
“price”: 15.5,
“isbnx”: 123456
}
set header as Header -ContentType-application/json
and set body as Body -raw-JSON
RESPONSE:
True
step3:
GET request fired again at http://127.0.0.1:5000/books
output:
list of all books including the NewBook found in JSON
2.7 Mistake to be aware of when using POST in Flask
Incase, we get some garbage data along with the actualdata, we should not consider or take it.
Say, if we got a request body passed with some garbage data, as below
{
“name”: “New Book with some Garbage”,
“price”: 15.5,
“isbn”: 123456,
“garbage”: 8hhsu8*$$^#VGGE
}
We should take the required data only, garbage data must be ignored.
#POST/books
@app.route(‘/books’, methods=[‘POST’])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
new_book = {
“name”: request_data[‘name’],
“price”: request_data[‘price’],
“isbn”: request_data[‘isbn’]
}
books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
return “True”
else:
return “False”
Runnable Code Snippet_2.7-Mistake to be aware while using POST in flask:
#Mistake to be aware while using POST in flask:
from flask import Flask, jsonify,request
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#data sanitizer
def validBookObject(bookObject):
#if entry already exists
for i in books:
if i["isbn"] == bookObject["isbn"]:
return False
else:
#if "keyword" in dictionary
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
new_book = {
"name": request_data['name'],
"price": request_data['price'],
"isbn": request_data['isbn']
}
books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
return "True"
else:
return "False"
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
2.8 Setting Status codes and Response bodies for POST requests
We returned “True” or return “False” strings/any strings in Flask and implicitly send a SAS code of “200 OK” back to the Client and content type of HTML back to the client as well.
In this case , we set “201” status code-> which indicates reuest has been fulfilled and resulted in one or more new resources being created.
Here we import ‘Response’ class, to invoke response constructor and set what is returned as response.
after inserting newbook to list, we add this Snippet
books.insert(0, new_book) #after this <-
Response{“par1″,”par2″,”par3”}
parameter 1-> Response body (we set this Empty)
parameter 2-> Status Code (we set this to 201)
parameter 3-> Content type header that will be sent back to Client(we send application/json and not HTML)
Once the Response constructor gets invoked, we save it to an Object called ‘response’ , as follows
response = Response{“par1″,”par2″,”par3”} #saved to Object
And instead of returning “True”, we return the response object itself
response = Response(“”,201,mimetype=’application/json’)
Finally we return the above response
Runnable Code Snippet_2.8-Setting Status codes and Response for POST request
#2.8 SettingStatuscodesandResponse
from flask import Flask, jsonify,request,Response
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#data sanitizer
def validBookObject(bookObject):
#if entry already exists
for i in books:
if i["isbn"] == bookObject["isbn"]:
return False
else:
#if "keyword" in dictionary
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
new_book = {
"name": request_data['name'],
"price": request_data['price'],
"isbn": request_data['isbn']
}
books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
response = Response("",201,mimetype='application/json')
return response
else:
return "False"
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
2.9 Setting location Headers for POST requests in Flask
Location Header points to where the Client can go and receive the new Resource that was created. In Flask Headers works same as KeyValue pair.
Client receives access to a resource it created not from the actual response body, but from a Location Header, and thats why we set the response body EMPTY in the previous code
After we get the response, we set the headers and call it as response.headers[], which takes a string ‘Location’ and we explicitly set what we want to be the header, as follows
reponse.headers[‘Location’] = <URL with the newly added isbn>
URL format: /books/isbn
Note here we will be using a RELATIVE PATH here, because the Client already knows what the Server is because they reaching it,
So we get newbook’s isbn and convert it to string as follows
“/books” + str(new_book[‘isbn’])
We convert the isbn to a string, so that it concatenates correctly.
The header is set and response.headers[‘Location’] = “/books” + str(new_book[‘isbn’]) is sent as header.
Runnable Code Snippet_2.9-Setting location Headers for POST requests in Flask
#2.9-Setting location Headers for POST requests in Flask
from flask import Flask, jsonify,request,Response
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#data sanitizer
def validBookObject(bookObject):
#if entry already exists
for i in books:
if i["isbn"] == bookObject["isbn"]:
return False
else:
#if "keyword" in dictionary
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
new_book = {
"name": request_data['name'],
"price": request_data['price'],
"isbn": request_data['isbn']
}
books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
response = Response("",201,mimetype='application/json')
response.headers['Location'] = "/books/" + str(new_book['isbn'])
return response
else:
return "False"
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
Postman Output after posting a body
{
“name”: “New Book”,
“price”: 15.5,
“isbn”: 64796
}
Response Window:
Location – http://127.0.0.1:5000/books/64796
We can see our LocationHeader has been updated
2.10 Handling invalid POST requests
Instead of returning “False” when invalid object is POST, we throw a meaningful Error Message and Status Code
invalidBookObjectErrorMsg = {
“error” : “Invalid book object in request”,
“helpString” : “Pls pass Data in similar format {‘name’:’bookname’, ‘price’:5.9, ‘isbn’:852569}”
}
response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype=’application/json’)
return response
Runnable Code Snippet_2.10 -Handling invalid POST requests
#2.10 - Handling invalid POST request
from flask import Flask, jsonify,request,Response, json
app = Flask(__name__)
print(__name__)
books=[
{
'name' : 'Book1',
'price' : 15.5,
'isbn' : 123456
},
{
'name' : 'Book2',
'price' : 12.2,
'isbn' : 789654
}
]
#request body
#{
# 'name' : 'Newbook',
# 'price' : 456,
# 'isbn' : 369852
#}
#data sanitizer
def validBookObject(bookObject):
#if entry already exists
for i in books:
if i["isbn"] == bookObject["isbn"]:
return False
else:
#if "keyword" in dictionary
if ("name" in bookObject and "price" in bookObject and "isbn" in bookObject):
return True
else:
return False
#Adding a POST route
#POST/books
@app.route('/books', methods=['POST'])
def add_book():
request_data = request.get_json()#get the req.data by get_json method
if(validBookObject(request_data)): #considering only validObject is sent
new_book = {
"name": request_data['name'],
"price": request_data['price'],
"isbn": request_data['isbn']
}
books.insert(0, new_book)#append new_bookObject we (sanitized)received from client,into BookList
response = Response("",201,mimetype='application/json')
response.headers['Location'] = "/books/" + str(new_book['isbn'])
return response
else:
invalidBookObjectErrorMsg = {
"error" : "Invalid book object in request",
"helpString" : "Pls pass Data in similar format {'name':'bookname', 'price':5.9, 'isbn':852569}"
}
response = Response(json.dumps(invalidBookObjectErrorMsg),status=400,mimetype='application/json')
return response
#GET/books
@app.route('/books')
def get_all_books():
return jsonify({'Books':books})
#GET/books/isbn
@app.route('/books/<int:isbn>')
def get_by_isbn(isbn):
return_value={}
for book in books:
if book["isbn"] == isbn:
return_value={
'name' : book["name"],
'price' : book["price"]
}
return jsonify(return_value)
app.run(port=5000)
Postman output is given below for reference,
Request body:
{
“name”: “Newbook”,
“price”: 123,
“isbnx”: 123
}
Status: 400 BAD REQUEST
Response body:
{
“error”: “Invalid book object in request”,
“helpString”: “Pls pass data in following format {‘name’:’Bookname’,’price’: 15.5,’isbn’: 456}”
}
So,…We have come to the end of this module, in the upcoming module we will deal with other HTTP methods to Update Data.
For Complete Sourcecode visit my Github repo: Link provided in the final module.
RS-codes

2 thoughts on “First time with Flask-Python -Module2”