Rest API with Flask
Overview
Today we will be introduced with Flask and will see how can we build REST API using Flask. We will go step by step to achieve that .
Installing Pipenv
Pipenv
is a dependency manager for Python projects. If you're familiar with Node.js's
npm
or Ruby's
bundler
it is similar in spirit to those tools. While
pip
can install python packages, Pipenv
is recommended as it’s a higher-level tool that simplifies dependency management for common use cases.Pipenv
automatically creates and manages a virtualenv for our projects, as well as adds/removes packages from our Pipfile as we install/uninstall packages.
It also generates the ever-important Pipfile.lock
, which is used to produce deterministic builds.
We will use Python 3
for our development and to to install pipenv
for python 3
run the following command in
terminal.You can check which Python version you have by typing into terminal/cmd python --version
.
Assuming, you have already installed Python version 3
. If not download and install it from
python.org
for your operating system.
pip3 install pyenv
Let's create a folder called rest_flask
and activate pipenv
mkdir rest_flask && cd rest_flask && pipenv shell
Install Dependencies
We will use flask , flask-sqlalchemy, flask-marshmallow and marshmallow-sqlalchemy as our dependencies and to install them type into your terminal:
pipenv install flask flask-sqlalchemy flask-marshmallow marshmallow-sqlalchemy
pipenv
will install our required dependencies and create a file named Pipfile
in which all the dependencies will be listed.
It looks a lot like package.json
of npm
but Pipfile
is of
toml
format.
Our Pipfile
looks like following now:
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
flask = "*"
flask-sqlalchemy = "*"
flask-marshmallow = "*"
marshmallow-sqlalchemy = "*"
[requires]
python_version = "3.7"
Hello TechyOwls!
Let's create a file called app.py
in our folder and write following code:
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
app = Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return jsonify({'msg': 'Hello Techyowls!'})
# Run Server
if __name__ == '__main__':
app.run(debug=True)
That's all we need to have our Hello Techyowls !
response and to run
python app.py
curl -X GET http://localhost:5000
Database Connection
SqlAlchemy is one of the popular ORM(Object Relational Mapper) for python and we will use SqlAlchemy for this project.In this tutorial I will illustrate how to use SqlAlchemy to connect with sqlite and mysql.
SQLite
baseDir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(baseDir, 'db.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Above code will create a file named db.sqlite when connection will be established.
MySQL
We will use a docker-compose file which will make mysql database available to be connected with our application.
If you want to know more about the docker-compose I have explained in great detail in another post Mysql Docker Compose our mysql.yaml compose file looks like this.
version: "3.7"
services:
freeway:
image: mysql:5.7.25
container_name: mysql
hostname: mysql
networks:
- default
volumes:
- techyowls/:/var/lib/mysql
environment:
- MYSQL_USER=techyowls
- MYSQL_PASSWORD=techyowls
- MYSQL_ROOT_PASSWORD=techyowls
- MYSQL_DATABASE=flask_rest
ports:
- "3306:3306"
command: mysqld --lower_case_table_names=1 --skip-ssl --character_set_server=utf8mb4 --explicit_defaults_for_timestamp
restart: always
volumes:
techyowls:
TODO: something
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:techyowls@localhost:3306/flask_rest'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Flask-MarshMallow
Flask-Marshmallow is a thin integration layer for Flask (a Python web framework) and marshmallow (an object serialization/deserialization library) that adds additional features to marshmallow, including URL and Hyperlinks fields for HATEOAS-ready APIs.
# Init flask-marshmallow
ma = Marshmallow(app)
Model Class
Let's define our model Product
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True)
description = db.Column(db.String(200))
price = db.Column(db.Float)
qty = db.Column(db.Integer)
def __init__(self, name, description, price, qty):
self.name = name
self.description = description
self.price = price
self.qty = qty
Schema
In Schema we define which fields we want to expose in json response, in our case we want them all.
class ProductSchema(ma.Schema):
class Meta:
fields = ('id', 'name', 'description', 'price', 'qty')
product_schema = ProductSchema()
products_schema = ProductSchema(many=True)
Table Creation
Now in the project directory open terminal and write
from app import db
db.create_all()
above command will create tables for all the defined models.
Resource End Points
POST
@app.route('/product', methods=['POST'])
def add_product():
name = request.json['name']
description = request.json['description']
price = request.json['price']
qty = request.json['qty']
new_product = Product(name, description, price, qty)
db.session.add(new_product)
db.session.commit()
return product_schema.jsonify(new_product)
GET
@app.route('/product', methods=['GET'])
def get_products():
all_products = Product.query.all()
result = products_schema.dump(all_products)
return jsonify(result)
GET By ID
@app.route('/product/<id>', methods=['GET'])
def get_product(id):
product = Product.query.get(id)
return product_schema.jsonify(product)
Put
@app.route('/product/<id>', methods=['PUT'])
def update_product(id):
product = Product.query.get(id)
name = request.json['name']
description = request.json['description']
price = request.json['price']
qty = request.json['qty']
product.name = name
product.description = description
product.price = price
product.qty = qty
db.session.commit()
return product_schema.jsonify(product)
DELETE
@app.route('/product/<id>', methods=['DELETE'])
def delete_product(id):
product = Product.query.get(id)
db.session.delete(product)
db.session.commit()
return product_schema.jsonify(product)
Our whole app.py file looks like bellow.
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
app = Flask(__name__)
baseDir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URL'] = 'sqlite:///' + os.path.join(baseDir, 'db.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
ma = Marshmallow(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), unique=True)
description = db.Column(db.String(200))
price = db.Column(db.Float)
qty = db.Column(db.Integer)
def __init__(self, name, description, price, qty):
self.name = name
self.description = description
self.price = price
self.qty = qty
class ProductSchema(ma.Schema):
class Meta:
fields = ('id', 'name', 'description', 'price', 'qty')
product_schema = ProductSchema()
products_schema = ProductSchema(many=True)
@app.route('/product', methods=['POST'])
def add_product():
name = request.json['name']
description = request.json['description']
price = request.json['price']
qty = request.json['qty']
new_product = Product(name, description, price, qty)
db.session.add(new_product)
db.session.commit()
return product_schema.jsonify(new_product)
@app.route('/product', methods=['GET'])
def get_products():
all_products = Product.query.all()
result = products_schema.dump(all_products)
return jsonify(result)
@app.route('/product/<id>', methods=['GET'])
def get_product(id):
product = Product.query.get(id)
return product_schema.jsonify(product)
@app.route('/product/<id>', methods=['PUT'])
def update_product(id):
product = Product.query.get(id)
name = request.json['name']
description = request.json['description']
price = request.json['price']
qty = request.json['qty']
product.name = name
product.description = description
product.price = price
product.qty = qty
db.session.commit()
return product_schema.jsonify(product)
@app.route('/product/<id>', methods=['DELETE'])
def delete_product(id):
product = Product.query.get(id)
db.session.delete(product)
db.session.commit()
return product_schema.jsonify(product)
if __name__ == '__main__':
app.run(debug=True)
Testing
Post Endpoint using curl
curl -d '{ "description": "IPhone XI", "name": "Iphone 11", "price": 600.0,"qty": 500}' -H "Content-Type: application/json" -X POST http://localhost:5000/product
Get ALL Product
python curl -X GET "http://localhost:5000/product"
[
{
"description": "IPhone XI",
"id": 1,
"name": "Iphone 11",
"price": 600.0,
"qty": 500
},
{
"description": "IPhone X",
"id": 2,
"name": "Iphone 10",
"price": 600.0,
"qty": 500
}
]
Get By ID
curl -X GET "http://localhost:5000/product/1"
{
"description": "IPhone XI",
"id": 1,
"name": "Iphone 11",
"price": 600.0,
"qty": 500
}
Update Endpoint
curl -X PUT -H "Content-Type: application/json" -d '{"name":"updated","description":"updated","price":200.0,"qty":2}' "http://localhost:5000/product/1"
{
"description": "updated",
"id": 1,
"name": "updated",
"price": 200.0,
"qty": 2
}
Conclusion
That's conclude your tutorial for creating rest api using Flask, in consecutive tutorials we will take our this knowledge one step further. Thanks for reading. See you in next post. Have a great day. The code used in in this tutorial can be found in github