Getting started with RESTful APIs using the Flask microframework for Python

Getting started with RESTful APIs using the Flask microframework for Python

In this tutorial we are going to learn how to create a simple REST API using Flask Python and SQL Alchemy. A good RESTful API structure imples the logical segmentation from the Models and the Data, but for this tutorial purpose we will create everything on the same file. One other thing that we will be using is a virtual environment because we can have a clean and isolated environment that doesn’t depend/share on other virtual environments' libraries and doesn’t access global installed libraries on your system.

System Configuration:

Let’s get started then:

Mac OS X:

$ sudo easy_install virtualenv 

or

$ sudo pip install virtualenv 

Ubuntu:

$ sudo apt-get install python-virtualenv

Super!! Let’s move on and create our project folder:

$ mkdir ratings
$ cd ratings

Now we just need to set up a new virtual environment:

You can choose another name for the environment instead of “venv”, this is just for demonstration purpose.

$ virtualenv venv

To start the above environment we just need to run the following command:

$ . venv/bin/activate

In our venv we will first install Flask framework:

(venv) ratings $ pip install Flask    

And next we will install Flask-SQLAlchemy:

(venv) ratings $ pip install Flask-SQLAlchemy    

Database Configuration:

Before we go any further we will have to do two things:

  1. Create a database called rating with one table called produts with two columns an INT rate and a VARCHAR name;
  2. Create a file on the project directory called rating_db.conf which will contain the database configuration:
[DB]
user: root  
password: root  
db: rating  
host: 127.0.0.1  

You have to enter your database data configuration, this is just an example for the tutorial.

For more information about Flask-SQLAlchemy you read it here: http://flask-sqlalchemy.pocoo.org/2.1/

Basic code configurations:

On the same directory as the rating_db.conf, let’s now create a python file rating.py. Now open it with your favorite editor or IDE and create this basic Flask Python structure:

from flask import Flask

application = Flask(__name__)

@application.route("/")
def hello():  
    return "Hello World!"

if __name__ == "__main__":  
    application.run()

What does all this mean?!

  1. The @application.route(“/”) defines a new route, which is translated as an url provided from the browser for example.

  2. The def hello() is the function that will answer to that route.

  3. What happens in the end? Well, everytime we hit the root of our project we will get a Hello World!.

Let's run a Simple HTTP Server to initialize our application and see this in action:

(venv) ratings $ python rating.py

If now hit the address created by the above command on your browser you will get the "Hello World!".

See! Easy! Congrats, you managed to create your first endpoint!

Now let's get back to the real deal!

This may seem confusing, but it's not. What we are doing here is nothing more than saying to the app where is our database configuration and establishing a new connection through the data provided in the file.

We are also mapping our table of products as a model in the code. We should separate the database model into a configuration file like I've mentioned before, but for this tutorial purpose we are going to map it directly on our file.

import ConfigParser  
from flask.ext.sqlalchemy import SQLAlchemy  
from flask import Flask, jsonify, request

application = Flask(__name__)

# Read config file
config = ConfigParser.ConfigParser()  
config.read('rating_db.conf')

# MySQL configurations
application.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://' + config.get('DB', 'user') + \  
                                     ':' + config.get('DB', 'password') + '@' + \
                                     config.get('DB', 'host') + '/' + config.get('DB', 'db')

application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True

mysql = SQLAlchemy()

# map models
class Products(mysql.Model):  
    __tablename__ = 'products'
    id = mysql.Column(mysql.Integer, primary_key=True)
    rate = mysql.Column(mysql.Integer, nullable=False)
    name = mysql.Column(mysql.String(128), nullable=False)

    def __repr__(self):
        return '<Products (%s, %s) >' % (self.rate, self.name)

@application.route("/")
def hello():  
    return "Hello World!"

if __name__ == "__main__":  
    application.run()

Coding time:

Before starting to code, if you want to know more about how flask endpoints structure work you can do it right here: http://flask.pocoo.org/docs/0.10/quickstart/

Alright, now we have the basics to start building a simple RESTful API. In this tutorial we will be doing a basic CRUD.

Let's add the new routes just bellow this

@application.route("/")
def hello():  
    return "Hello World!"

CREATE

Like the title suggests, we are going to create a new product in our table of products using a POST method:

@application.route('/product', methods=['POST'])
def createProduct():

    # fetch name and rate from the request
    rate = request.get_json()["rate"]
    name = request.get_json()["name"]

    product = Products(rate=rate, name=name) #prepare query statement

    curr_session = mysql.session #open database session
    try:
        curr_session.add(product) #add prepared statment to opened session
        curr_session.commit() #commit changes
    except:
        curr_session.rollback()
        curr_session.flush() # for resetting non-commited .add()

    productId = product.id #fetch last inserted id
    data = Products.query.filter_by(id=productId).first() #fetch our inserted product

    config.read('rating_db.conf')

    result = [data.name, data.rate] #prepare visual data

    return jsonify(session=result)

READ

Fetches all data using a request method GET.

@application.route('/product', methods=['GET'])
def getProduct():  
    data = Products.query.all() #fetch all products on the table

    data_all = []

    for product in data:
        data_all.append([product.id, product.name, product.rate]) #prepare visual data

    return jsonify(products=data_all)

This function basically fetches all the data in our table of products into an array "data_all" which later will be iterated in order to display all our products for the user to see.

UPDATE

Why patch instead of put? Well since we are updating an existing product we should always use a PATCH method, as the PUT method is used for replacing a resource in it's entirety.

Hint:

<int:productId> means we are expecting to receive a dynamic argument in the url.

e.g.: http://127.0.0.1:5000/4/product

@application.route('/<int:productId>/product', methods=['PATCH']) 
def updateProduct(productId):

    rate = request.get_json()["rate"] #fetch rate
    curr_session = mysql.session

    try:
        product = Products.query.filter_by(id=productId).first() #fetch the product do be updated
        product.rate = rate #update the column rate with the info fetched from the request
        curr_session.commit() #commit changes
    except:
        curr_session.rollback()
        curr_session.flush()

    productId = product.id
    data = Products.query.filter_by(id=productId).first() #fetch our updated product

    config.read('rating_db.conf')

    result = [data.name, data.rate] #prepare visual data

    return jsonify(session=result)

DELETE

This is a very simple function. We get and id of a product, we delete it. How?!

@application.route('/product/<int:productId>', methods=['DELETE'])
def deleteProduct(productId):

    curr_session = mysql.session #initiate database session

    Products.query.filter_by(id=productId).delete() #find the product by productId and deletes it
    curr_session.commit() #commit changes to the database

    return getProduct() #return all create products

More concrete, we get the productId from the url (int:productId) that belongs to a product in our table, pass the productId to our function, find the product and delete it. Thats it!

In the end we call the function "getProduct()" and display the remaining products for the user to see.

Hints:
  1. You can use postman or DHC to simulate the requests and see your API working.

  2. If you want to know more about HTTP methods check this link: http://www.restapitutorial.com/lessons/httpmethods.html

comments powered by Disqus