Flask is a micro framework that suits very much if you want to build a lightweight backend for your mobile apps. Not only this but it is also helpful in creating a complete website. The benefit is that it doesn’t include so many libraries out of the box. However you can plug-in libraries as needed in your project. In this tutorial, we are building API with Python Flask.
High level comparison between Flask and Django
- If you are not an experienced Python developer, you can get started with Django faster. The reason is that out of the box Django provides all the essential components of a web app.
- This means that the Django app is already big size with a lot of dependencies which you might even not need.
- On the contrary, Flask has only a few dependencies. There are also a few more optional dependencies but those are not installed by default. You can install optional dependencies if needed.
- This way Flask gives you more flexibility in choice but at the same time requires you to spend time in finding libraries that fit your need. This means that a less experienced developer may have to spend a bit more time in finding suitable libraries.
Create Virtual Environment
- Create virtual environment using
venv
or using Virtualenvwrapper (see my how-to guide to setup it on Mac OS)
Install Flask
- Create a folder for your application. Let us name it
flask_api_demo/
. - Run following command in Terminal while you are inside above created folder.
> pip install Flask
Check what dependencies have been installed
As I wrote above, Flask installs minimal dependencies. You can verify that by running following command.
> pip freeze
This command shows you the installed dependencies in your current virtual environment. The list you’d see should be like:
Click==7.0
Flask==1.1.1
itsdangerous==1.1.0
Jinja2==2.11.1
MarkupSafe==1.1.1
Werkzeug==1.0.0
So only five dependencies installed other than Flask itself!
Create database
This example uses PostgreSQL database. You can use other databases supported by SQLAlchemy, for example MYSQL, SQLite etc, but you’ll have to accordingly make changes in .env
and config.py
files in next steps.
So go ahead, create a database and name it as you like!
Install python-dotenv
and create .env file
Install python-dotenv which will enable you to create a .env
file to put all your ENV
variables in dev environment.
> pip install python-dotenv
While you are still at the root level of flask-api-demo/
, create .env
file with following content in it.
FLASK_ENV=development
FLASK_DEBUG=true
APP_BASE_URL=127.0.0.1
APP_SECRET_KEY=dev
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=<db_name>
DB_USERNAME=<username>
DB_PASSWORD=<password>
SESSION_LENGTH=2592000
Replace <db_name>
, <username>
and <password>
with valid values.
Add Config file
- Create
config.py
underflask-api-demo/
. - In this file, create
Config
class with following content.
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
ENV = os.getenv('FLASK_ENV', 'development')
FLASK_DEBUG = os.getenv('FLASK_DEBUG', True)
SECRET_KEY = os.getenv('APP_SECRET_KEY', 'dev')
BASE_URL = os.getenv('APP_BASE_URL', '127.0.0.1')
DATABASE_HOST = os.getenv('DB_HOST')
DATABASE_PORT = os.getenv('DB_PORT')
DATABASE_NAME = os.getenv('DB_NAME')
DATABASE_USERNAME = os.getenv('DB_USERNAME')
DATABASE_PASSWORD = os.getenv('DB_PASSWORD')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = 'postgres://{username}:{password}@{host}:{port}/{db_name}'.format(
username=DATABASE_USERNAME,
password=DATABASE_PASSWORD,
host=DATABASE_HOST,
port=DATABASE_PORT,
db_name=DATABASE_NAME
)
SESSION_LENGTH = int(os.getenv('SESSION_LENGTH'))
Define User
and AuthToken
models
- Under
app/
folder, add a new filemodels.py
. - Add
User
model in this file.
from . import db
class User(db.Model):
__tablename__ = 'users'
id = db.Column(
db.Integer,
primary_key=True
)
username = db.Column(
db.String(64),
index=False,
unique=True,
nullable=False
)
password = db.Column(
db.String(64),
index=False,
unique=False,
nullable=False
)
email = db.Column(
db.String(80),
index=False,
unique=True,
nullable=False
)
created_at = db.Column(
db.DateTime,
index=False,
nullable=False
)
updated_at = db.Column(
db.DateTime,
index=False,
nullable=False
)
auth_tokens = db.relationship('AuthToken', backref='user')
def __repr(self):
return '<User {}>'.format(self.username)
class AuthToken(db.Model):
__tablename__ = 'auth_tokens'
id = db.Column(
db.Integer,
primary_key=True
)
user_id = db.Column(
db.Integer,
db.ForeignKey('users.id'),
nullable=False
)
token = db.Column(
db.String(64),
nullable=False,
unique=True
)
expires_at = db.Column(
db.DateTime,
index=False,
nullable=False
)
created_at = db.Column(
db.DateTime,
index=False,
nullable=False
)
user_id
field in AuthToken
is a foreign key referencing the id
field in User
model. We have also defined a backref
property in User
model:
auth_tokens = db.relationship('AuthToken', backref='user')
This lets us accessing the auth_tokens
assigned to a User
with user.auth_tokens
.
Create app/
folder and __init__.py
file
- Create
app/
folder underflask-api-demo/
.
(The nameapp
is my own choice, you can name it differently if you want to). - Create
__init__.py
file underapp/
. - Add following code to
__init.py
.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
db.init_app(app)
with app.app_context():
from . import models
db.create_all()
return app
Inside create_app
function, we are instantiating app
, a Flask app. We are also loading app.config
from Config
file we created above. Then we load the db
instance with app
configs (we instantiated db
above create_app
function and it will be importable in other files/modules.
With db.create_all()
call, all the tables for all the models defined in models.py
will get created. So far we have defined only User
model, so this function call will create one table users
(as the name specified using __tablename__
property of User
model.
Create DB tables
Execute the following command in your terminal.
> flask run
When you run this command, the code inside __init__.py
gets executed and following happens.
- Flask’s dev server starts serving the app on
http://127.0.0.1:5000/
- With execution of
db.create_all()
, tables associated with both of our models get created.
Now you can check your database, if everything was done correctly so far, there should be two tables users
and auth_tokens
.
Create api.py
Now, let us build our first Python Flask API endpoints. Under app
folder create api.py
file and add following import
statements.
import hashlib
import random
import string
from flask import request
from flask import current_app as app
from .models import db, User, AuthToken
from datetime import datetime, timedelta
User registration endpoint
In api.py
, create /api/users
route and allow POST
method on this path with create_user
function.
@app.route('/api/users', methods=['POST'])
def create_user():
username = request.form.get('username')
password = request.form.get('password')
email = request.form.get('email')
if username != None and password != None and email != None:
response = None
try:
new_user = User(
username=username,
password=hashlib.sha256(str(password).encode('utf-8')).hexdigest(),
email=email,
created_at=datetime.now(),
updated_at=datetime.now()
)
db.session.add(new_user)
db.session.commit()
response = response = {
'status': 'success',
'token': 'User regisrtered successfully.'
}
except Exception as error:
response = response = {
'status': 'failed',
'token': 'User registration failed.'
}
return response
This function will be call when server receives a POST request on /api/users
path. If all required parameters are available in request, it creates a new user. For password
field, we are using sha256
hash.
Login endpoint
Create /api/login
route accepting POST
requests. To do so add following function in api.py
.
@app.route('/api/login', methods=['POST'])
def authenticate():
username = request.form.get('username')
password = request.form.get('password')
response = None
try:
authenticated = User.query.filter(
User.username == username,
User.password == hashlib.sha256(str(password).encode('utf-8')).hexdigest()
).first()
if authenticated != None:
token = ''.join([random.choice(string.ascii_letters + string.digits) for n in range(64)])
if len(authenticated.auth_tokens) > 0:
token = authenticated.auth_tokens[0].token if authenticated.auth_tokens[0].expires_at <= datetime.now() \
else authenticated.auth_tokens[0].token
else:
auth_token = AuthToken(
user_id=authenticated.id,
token=token,
expires_at= datetime.now() + timedelta(seconds=app.config.get('SESSION_LENGTH')),
created_at=datetime.now()
)
db.session.add(auth_token)
db.session.commit()
response = {
'status': 'success',
'token': token
}
else:
response = {
'status': 'failed',
'message': 'Login failed'
}
except Exception as error:
response = {
'status': 'failed',
'message': 'An error occured: {}'.format(error)
}
return response
It reads username
and password
sent in POST
request and looks into DB if the username
, password
combination matches any existing user. If yes, it generates a token (if there is no existing valid token) for the authenticated user.
On successful authentication and token generation, it returns the success response containing a token
otherwise, the response contains a failure message.
Import api.py
in __init__.py
To make the routes available, import api.py
under app_context
in __init__.py
. Now, __init__.py
should look like this:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__, instance_relative_config=False)
app.config.from_object('config.Config')
db.init_app(app)
with app.app_context():
from . import models
from . import api
db.create_all()
return app
Testing the API endpoints
Use Postman or any other API client tool to test both endpoints of our Python Flask API. 🙂


Code
You can get the code from my Github repo: flask-api-example