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:
- Create a database called rating with one table called produts with two columns an INT rate and a VARCHAR name;
- 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?!
-
The @application.route(“/”) defines a new route, which is translated as an url provided from the browser for example.
-
The def hello() is the function that will answer to that route.
-
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:
-
You can use postman or DHC to simulate the requests and see your API working.
-
If you want to know more about HTTP methods check this link: http://www.restapitutorial.com/lessons/httpmethods.html