Rest API with Flask

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  

Hello TechyOwls!

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

Previous
Next
Avatar
Moshiour Rahman
Software Architect

My interests include enterprise software development, robotics, mobile computing and programmable matter.

comments powered by Disqus
Previous
Next

Related