889 words
4 minutes
Setup a Simple API Using Python and Flask – Part 2

In this article — a continuation of Part 1 — I’ll cover some more advanced topics I previously mentioned. To begin, we’ll implement a simple login/register system where clients can register, log in using their credentials, and check their profile. So, let’s get started!

Flask Global Variables#

Flask provides several global variables that can simplify your development workflow. For example, the request object is a global variable that stores data related to HTTP requests — such as URL, query parameters, headers, and more.

You can import Flask global variables in two ways:

from flask import globals # imports all global vars

Or, if you only need specific ones (recommended for cleaner code):

from flask import request

To see how we can access incoming data, let’s create a simple /test route:

@app.route('/test', methods=['GET'])
def test():
name = request.args.get('name')
username = request.args.get('username')
password = request.args.get('password')
return f"Hello, {name}! Your username is {username} and your password is {password}"

This route accepts only GET requests. You can access the parameters sent by the client via request.args.get('field_name').

For example, accessing this URL:

http://localhost:5000/test?name=Armin&username=mynameisarmin&password=mysafepassword

Will return:

Hello, Armin! Your username is mynameisarmin and your password is mysafepassword

Ways to Access Request Data#

  • request.data: raw data as a string when Flask can’t handle the mimetype.
  • request.args: key/value pairs in the query string.
  • request.form: key/value pairs in the request body (e.g. from HTML forms).
  • request.files: uploaded files.
  • request.values: combines args and form, preferring args if duplicate keys exist.
  • request.json: parsed JSON data (must have Content-Type: application/json).

These objects are typically instances of MultiDict, except for request.json.

You can access values with:

  • request.form['name']: use this if you’re sure the key exists.
  • request.form.get('name'): safer if the key might not exist.
  • request.form.getlist('name'): use this if the same key is submitted multiple times.

TinyDB#

To build the login/register system, we need a way to store user credentials. Rather than using a full-fledged database like MongoDB or Redis for this small demo, we’ll use TinyDB, a lightweight, document-oriented database that stores data in a JSON file.

“TinyDB is a document-oriented database that stores data in a JSON file. It’s the closest thing I’ve found to a NoSQL version of SQLite. It’s simple, lightweight, serverless, and extensible.” — lyz-code.github.io

To install TinyDB:

Terminal window
pip install tinydb

We’ll also write a helper class to simplify using TinyDB. Create a file called db.py with the following content:

from tinydb import TinyDB, Query
import os
class Database:
def __init__(self, dbName, dbPath):
self.dbName = dbName
self.dbPath = dbPath if dbPath.endswith('/') else dbPath + '/'
self._initialize_db_path()
self.db = TinyDB(self.dbPath + self.dbName, indent=4, sort_keys=True, separators=(',', ':'))
def _initialize_db_path(self):
if not os.path.exists(self.dbPath):
os.makedirs(self.dbPath)
def query(self, table, query):
return self.db.table(table).search(query)
def insert_or_update(self, table, data):
table_ref = self.db.table(table)
table_ref.upsert(data, Query().id == data['id'])
return True
def insert(self, table, data):
self.db.table(table).insert(data)
return True
def close(self):
self.db.close()

Then import your helper and Flask’s jsonify in your main file:

from db import *
from flask import jsonify

Create the database instance after initializing your Flask app:

app = Flask(__name__)
myDatabase = Database('db.json', 'database/')

Register Route#

@app.route('/register', methods=['POST'])
def register():
name = request.json['name']
username = request.json['username']
password = request.json['password']
if myDatabase.query('users', Query().username == username):
return jsonify({"error": "Account already exists"})
myDatabase.insert('users', {'name': name, 'username': username, 'password': password})
return jsonify({'status': 'success'})

This route accepts JSON data, checks if a user with the same username already exists, and if not, inserts the user into the database.

Testing the Endpoint#

  • For GET requests: just open the URL in your browser.
  • For POST requests: use Postman, curl, or online tools like ReqBin.

Tip: Postman is more powerful and user-friendly, but ReqBin is great if you don’t want to install anything.

Login Route#

@app.route('/login', methods=['POST'])
def login():
username = request.json['username']
password = request.json['password']
user = myDatabase.query('users', Query().username == username and Query().password == password)
if user:
return jsonify({'status': 'success'})
return jsonify({'error': 'Account does not exist'})

JWT Integration#

JWT (JSON Web Token) is a secure way to transfer identity and authorization information.

Install the extension:

Terminal window
pip install Flask-JWT-Extended

Import what we need:

from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, verify_jwt_in_request

Configure your app:

app.config["JWT_SECRET_KEY"] = "super-secret" # Replace with a secure key!
jwt = JWTManager(app)

You can generate a random key using: https://random.justyy.workers.dev/api/random/?cached&n=32

Updated Login Route with JWT#

@app.route('/login', methods=['POST'])
def login():
username = request.json['username']
password = request.json['password']
user = myDatabase.query('users', Query().username == username and Query().password == password)
if user:
return jsonify({
'status': 'success',
'token': create_access_token(identity=username)
})
return jsonify({'error': 'Account does not exist'})

On success, you’ll receive a JWT in the response.

Index Route (JWT Protected)#

@jwt_required()
@app.route('/index', methods=['GET'])
def index():
verify_jwt_in_request()
current_user = get_jwt_identity()
if current_user:
return jsonify({'status': 'success', 'username': current_user})
return jsonify({'error': 'Invalid token'})

Send the JWT token in the Authorization header like: Authorization: Bearer <your-token>

Full Source Code#

from flask import Flask, request, jsonify
from db import *
from flask_jwt_extended import create_access_token, get_jwt_identity, jwt_required, JWTManager, verify_jwt_in_request
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "super-secret" # Replace this!
jwt = JWTManager(app)
myDatabase = Database('db.json', 'database/')
@app.route('/', methods=['GET'])
def home_page():
return "Tada! My simple Flask app is running!"
@app.route('/register', methods=['POST'])
def register():
name = request.json['name']
username = request.json['username']
password = request.json['password']
if myDatabase.query('users', Query().username == username):
return jsonify({"error": "Account already exists"})
myDatabase.insert('users', {'name': name, 'username': username, 'password': password})
return jsonify({'status': 'success'})
@app.route('/login', methods=['POST'])
def login():
username = request.json['username']
password = request.json['password']
user = myDatabase.query('users', Query().username == username and Query().password == password)
if user:
return jsonify({
'status': 'success',
'token': create_access_token(identity=username)
})
return jsonify({'error': 'Account does not exist'})
@jwt_required()
@app.route('/index', methods=['GET'])
def index():
verify_jwt_in_request()
current_user = get_jwt_identity()
if current_user:
return jsonify({'status': 'success', 'username': current_user})
return jsonify({'error': 'Invalid token'})
app.run(host='localhost', port=5000)

Conclusion#

In this final article, I’ve introduced more advanced concepts like authentication and token-based authorization using JWTs. I hope it was helpful! If you have any questions or comments, feel free to leave them below — I’ll respond as soon as I can.