ePrivacy and GPDR Cookie Consent by Cookie ConsentAn Application Structure for Python REST APIs - Doobries Domain

An Application Structure for Python REST APIs

— 9 minute read

A picture of multicoloured flasks

Introduction permalink

In this article, I'm going to show the structure that I use for REST based APIs using Python. In Python, I use Flask to create APIs as I find this is easy to use, yet very powerful. This article isn't a tutorial on using Flask, rather I'm showcasing the project structure I currently use that allows me to develop and test Flask applications easily.

If you've never used Flask before, its a lightweight framework that allows Python developers to create both Web Applications and Web APIs. It includes all the tools necessary and allows you to write easily testable applications. Flask can easily be installed into an application using pip

pip install flask

Top level structure permalink

The basic structure I like to use is shown below.

├── application
│ ├── api
│ │ ├──
│ │ └──
│ ├── factory.py
│ ├──
├── app.py
├── Makefile
├── requirements.txt
└── tests
├── api
│ ├──
│ └──
├── context.py
└── test_.py

Within this structure, there are two top level folders, application and tests. These are used for storing the application code and the test code respectively. I like to keep the test code separate from the application code and use this structure rather than placing the tests in the same folder as the application code.

Within the application and tests folders, there are individual files for each api I want to develop. Each of these corresponds to a different url.

Main Python Classes permalink

Within this directory structure, there are 3 Python files that are standard across each application:

  • app.py
  • application/factory.py
  • tests/context.py

Lets take a look at each of these in turn.

app.py permalink

This is the main file to bootstrap the application and invokes the create_app() method in the application/factory.py class:

from application.factory import create_app

app = create_app()

if __name__ == '__main__':

application/factory.py permalink

from flask import Flask
from application.api.moon import moon
from application.api.sun import sun

def create_app():
app = Flask(__name__)
app.register_blueprint(api1, url_prefix=<api1>.url_prefix)
app.register_blueprint(api2, url_prefix=<api2>.url_prefix)

return app

This file creates a Flask application and registers the Blueprints for the different API endpoints.

Flask describes a blueprint as

a set of operations which can be registered on an application, even multiple times.

Essentially in this example, this means that we can register a set of APIs within the application with each API being stored in a separate file. This allows us to easily create larger applications whilst maintaining smaller testable components.

tests/context.py permalink

This file allows the tests to be able to import the application logic when the tests are stored in a different folder to the applicaton source code.

import application
import os
import sys

sys.path.insert(0, os.path.abspath(
os.path.join(os.path.dirname(__file__), '..')))

An example API permalink

Using the ephem library and the basic API structure above, lets see how we can write an API to return the phase of the moon on a given date.

Defining Endpoints permalink

To obtain the phase of the moon, I'm going to create three endpoints:

  • /moon/
  • /moon/phase
  • /moon/phase/yyyy/mm/dd

To create the API, create a file called application/api/moon.py. To initialise the API with Flask, we need to register the Blueprint:

from flask import Blueprint, Response
import json
import ephem
from ..moonphase import MoonPhase
import datetime
from datetime import date

moon = Blueprint('moon', __name__)
moon.url_prefix = '/moon'

This creates a Blueprint called moon at the endpoint of /moon

To create the first of our endpoints (/moon/), we need to define a method with a route of / (as the API itself has the url_prefix of /moon).

def index():
return Response(json.dumps("Moon API"), mimetype=APPLICATION_JSON)

To create our second endpoint (/moon/phase), we're going to return the Moon phase for the current date. This can be achieved with the following code:

def phase_today():
m = ephem.Moon()
today = date.today()
return Response(json.dumps(MoonPhase(round(m.moon_phase * 100, 1), today.year, today.month, today.day).__dict__),

In this code, the method is decorated with the route /phase making the url end with /moon/phase. This method uses the json.dumps method to return a JSON representation of an instance of a MoonPhase class as the response.

MoonPhase is a @dataclass defined within the application/moonphase.py file

from dataclasses import dataclass

class MoonPhase:
'''Data object to be passed out from the api to the caller'''
phase: float
year: int
month: int
day: int

Finally, getting back to moon.py, the final API endpoint (/moon/phase/yyyy/mm/dd) can be defined as:

def phase_specific_day(year, month, day):
m = ephem.Moon()
return Response(json.dumps(MoonPhase(round(m.moon_phase * 100, 1), year, month, day).__dict__),

This method is almost identical to the previous, except that the year, month and day are passed in as parameters into the REST API.

Testing the API permalink

To test the API, we can use PyTest and write a test within the tests/api folder, for example:

from application.factory import create_app
import pytest
from ..context import application

def client():
return create_app().test_client()

def test_home_route(client):
response = client.get('/moon/')
assert response.get_json() == 'Moon API', 'Route should return "Moon API"'

def test_moon_phase_today(client):
response = client.get('/moon/phase')
assert response.status_code == 200, 'Invalid status code'
assert float(response.get_json()['phase']) > 0.0, 'Invalid phase'
assert int(response.get_json()['year']) > 0, 'Invalid year'
assert int(response.get_json()['month']) > 0, 'Invalid month'
assert int(response.get_json()['day']) > 0, 'Invalid day'

Conclusion permalink

In this article, I've shown a standard template that can be used to develop and test Flask applications. I've then shown an example of adding an API using this template.

The code is stored in a modular fashion so the application can easily be enhanced with more APIs. Also, test application is structured so that tests can easily be written for each of the APIs and other classes within the application.

The complete source code associated with this article is available on GitHub

Credits permalink

Photo by Raghav Bhasin on Unsplash