top of page

Python Frameworks and REST API

In this article, I perform a comparative study on building a basic REST API using major Python-based frameworks — Django, Flask, and FastAPI.

what is REST?

REpresentational State Transfer is a set of standards that defines how operations on web-apps access data following a group of predefined operations. It has been accepted ubiquitously due to its simplicity and efficiency.

REST API provides various options for a request depending on the type of operation. The two most common requests are:

  1. GET — This request may or may not provide some filter parameters in the URI, which returns the matching data.

  2. POST — This request creates a new record in the data. The parameters are provided in the request body. RFC 7231 defines that a successful POST request should — “send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created and a representation that describes the status of the request while referring to the new resource(s)”

Other operations may use PUT, DELETE, HEAD, OPTIONS, and PATCH requests.

Django: The star-studded all-rounder.

Django is undoubtedly one of the most powerful frameworks for web development. Exploiting the power (flexibility, ease of use, community support) of Python is a secure and scalable framework widely accepted by the community. I have worked on various frameworks and languages; Django’s admin panel and ORM (Object-Relational Mapper) provide robust development and easier testing.

Let’s build a basic REST API on Django using DRF. (Note: It is possible to build a REST API without DRF, but it is always preferred to use DRF since it makes a lot of other operations such as security, access control, and serialization easy and synchronized with the Django ORM)

Assuming you have Django installed already. Let’s start by installing DRF in your virtual environment.

source myenv/bin/activate
pip install djangorestframework

Now, we add our dependency (DRF) in settings.py

# imports
# setting

INSTALLED_APPS= [
    'main_app',
    'rest_framework',
]

Next, we are going to write the views for displaying our data. Consider our models.py to consists of class Person(name, age, email). We need to display this Model in the form of JSON since we are making a REST API. This process is known as serialization. Though you can use the serializer available indjango.core, DRF provides us with serialization that can validate and process complex data easily.

Let’s createmain_app/serializers.py

from rest_framework import serializers
from .models import Person

class PersonSerializer ( serializers.Serializer):
#   name = serializer.CharField(max_length=255)
#   age = serializer.IntegerField(min_value=18, max_value=120)
#   email = serializer.EmailField()

    class Meta:
        model = Person
        fields='__all__'

The serializer automatically retrieves the fields from models and creates a serializer for the Person class. Let’s use this serializer to list our Person objects. DRF provides us with generic views that are commonly used in web development. For this example, we will be using ListCreateAPIView, various other generic views that can be found in the DRF documentation here. (Note: The default methods can be overridden to provide an additional layer of customization)

An example with the class-based views is:

from rest_framework import generics
from .serializers import PersonSerializer
from .models import Person

class PersonListCreateAPIView(generics.ListCreateAPIView):
    queryset = Person.objects.all() #customize your query as you please
    serializer_class = PersonSerializer

If you prefer customizing using function-based views, you can define functions get(), post(), create() that override the default functions and you can modify them accordingly.

Thus we see how using DRF’s generics reduces the amount of effort required to create mundane views and even perform access control and serialization. Next, we jump on to Flask.


Flask: the easy prototyping framework.

Flask is a VERY simple framework and yet a powerful python web framework. Flask does not natively provide an ORM and requires to perform a manual connection to the DB. Nevertheless, a Flask API can be created in a single file since it is really simple to setup for prototyping applications.

Assuming you already have Flask installed in your virtual environment, let’s start by creating our app.py . First, we make the necessary imports and start a Flask app. To design a ListCreate view, we provide a parameter to the app.route decorator called methods=[‘GET’,’POST’] defines the allowed request methods on this route. We use flask.jsonify to convert our response in JSON, we divide our code of listing and creation based on the type of request we receive.

#Por Flask
import flask
from flask import request, jsonify
import sqlite3

app=flask.Flask(__name__)
app.config["DEBUG"] =True

def dict_factory(cursor, row):
    d= {}
    for idx, colinenumerate(cursor.description):
        d[col[0]] =row[idx]
    return d

@app.route('/api/v1/resources/persons/', methods=['GET','POST'])
def personlistcreateview:
    conn=sqlite3.connect('persons.db')
    conn.row_factory=dict_factory
    cur=conn.cursor()
    if flask.request.method== 'GET':
        all_books=cur.execute('SELECT*FROM books;').fetchall()
        return jsonify(all_books)
    else:
    #       request is POST
        try:
            name=request.form['name']
            age=request.form['age']
            email=request.form['email']
    
            with sql.connect("persons.db") as con:
                cur=con.cursor()
                cur.execute("INSERTINTOpersons (name,age,email) 
                VALUES (?,?,?,?)",(name, age, email) )
                con.commit()
            return jsonify({"Success":True}), 201
            return jsonify({"Success":False}), 500

@app.errorhandler(404)
    def page_not_found(e):
        return "<h1>404</h1><p>The resource could not be found.</p>", 404

app.run()

As we see, Flask is easier and faster to develop, but as the project gets more complex and scales, Flask falls short due to its lack of features. But for small projects, Flask provides a better and easier experience. Next, we study our newest member of the club — FastAPI.


FastAPI — The name says it all.

FastAPI is a recently developed Python-based web framework that is based on Starlette and Pydantic. For those who don't know about Starlette, it is an ASGI (Asynchronous Server Gateway Interface) framework based on Python. Traditionally, WSGI (Web Server Gateway Interface) has been used to run and host python codes on the web. ASGI is lightweight and natively supports async requests which can be used to improve load handling and response time. Pydantic on the other hand is data validation library that uses Python type hinting.

Add alt text

Benchmarks at teachempower.com show that FastAPI is in fact the fastest major Python-based framework.


FastAPI is indeed really fast. It natively supports the following functionalities:-

  1. OpenAPI documentation using two default options — SwaggerUI and ReDoc.

  2. RESTful

  3. Parameter Validation using Pydantic

  4. GraphQL support

  5. Easy Testing


Let’s start with making a basic REST API using FastAPI

pip install fastapi

Installs FastAPI in our venv

Thanks to pydantic, we can define our schema using pydantic and use sqlalchemy to have an ORM that can perform CRUD operations from our app (just like Django does ;-;)

pip install sqlalchemy
pip install

Let’s structure our project in the following manner: | — main.py Entrypoint for the service | — models.py | — db.py connects to db | — CRUD.py contains the functions to perform Create Read Update and Delete operations

We are focusing on developing the REST API, so I will be skipping the other files for now but will be covering them later on in a future edit. More details on here.

#FastAPI

import uvicorn
from typing import Optional
from fastapi import Depends, FastAPI, HTTPException
from starlette import status
from pydantic import BaseModel

# make a file crud.py that contains the major crud (create read update delete) operations using sqlalchemy ORM

import crud

app=FastAPI()

# Model
class Person(BaseModel):
    name: str
    age: int
    email: Optional[str] =None

def get_db():
#     write a code to get db

@app.post("/person", response_model=Person)
async def create_new_person(person: Person, db: get_db):
    db=get_db()
    db.create(person)
    return person

@app.get("/person")
async def get_all_persons(db: get_db):
    #perform a sql query using sqlalchemy to get all objects of persons
    return crud.get_all_persons(db=db)

if __name__=="__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

Response_model can be used to change the output schema in some cases, such as when creating new users, you do not want to return the password in the input model.

We run the main.py file now with

uvicorn main:app

That’s it! FastAPI also makes testing easy using Pytest. Along with that, it also provides OpenAPI docs and information about your models and their validations using Pydantic. FastAPI deserves more attention and can be adopted to deploy scalable, fast, and easy-to-maintain servers quickly.


Source: Medium


The Tech Platfrom

0 comments

Recent Posts

See All
bottom of page