Roy Portas
Roy Portas
POSTSPROJECTS

Building a REST API with Python, Flask and SQLAlchemy - Part 1

Published 2020-12-20

I wanted to build a simple API and web app for meal planning with a twist. I wanted a service which could generate a weekly recipe list, finding the best way to reuse ingredients to minimize the amount of food waste and ingredients I have to buy from the shops.

To do this, I wanted to try out some frameworks I haven't used in a while, namely:

  • Flask for the API
  • SQLAlchemy for the ORM

The point of this series is to provide a step by step guide to building a fully fledged API. In Part 1 we will setup Flask and add a sample route, as well as using python-dotenv for loading in configuration.

Setup

I'm wanted to give poetry a go for dependency management in this project, I followed the instructions page to install it.

To get started, I'm gonna create a new project using

poetry new backend

This will create the following directory structure:

backend
├── backend
│   └── __init__.py
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_backend.py

Neat! Looks like a sensible file structure to get started with, now lets add some libraries we are using.

If your new to Python, the folder structure might look new to you, heres the important bits:

  • The backend and tests directories are Python package (denoted by the __init__.py file)
  • pyproject.toml is used by Poetry to manage the dependencies

Now lets add the Flask package with the following command in a terminal

 poetry add Flask

Next I'll add a basic Flask app to test everything out, which will just be the minimal example from Flask's documentation, which will be added to backend/__init__.py

# backend/__init__.py

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

which can be tested by running the following in a terminal.

NOTE: If your on Windows, the export part will be different, see here for info

[roy@localhost backend]$ export FLASK_APP=backend
[roy@localhost backend]$ poetry run flask run
 * Serving Flask app "backend"
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

In a new terminal, we can test the app by curling the following address

[roy@localhost backend]$ curl localhost:5000
Hello, World!

It worked! Awesome! Its worth noting that the above command should only be used for development, Flask has some documentation for deploying it properly

Additional Reading:

Structuring Applications with Blueprints

Blueprints allow you split up your routes, which is handy for dividing your application into more maintainable bits, lets start by making a super simple blueprint called ingredients that we will build upon later.

Lets start by creating the following directory structure, the bolded bits are new

backend
├── backend
│   ├── __init__.py
│   └── modules│       └── ingredients.py├── poetry.lock
├── pyproject.toml
├── README.rst
└── tests
    ├── __init__.py
    └── test_backend.py

So this is what happened:

  • Created the modules directory in the backend package
  • Create a blank [ingredients.py](http://ingredients.py) file

Now lets create a blueprint inside the [ingredients.py](http://ingredients.py) file

# backend/modules/ingredients.py
from flask import Blueprint

ingredients = Blueprint("ingredients", __name__)

@ingredients.route("/")
def get_ingredients():
    return "Hello world from ingredients

This will create a basic blueprint for things related to ingredients, at the moment it contains just one route, mounted at /

Now lets use the blueprint in our application by making the following changes to backend/__init__.py

# backend/__init__.py
__version__ = '0.1.0'

from flask import Flask
from .modules.ingredients import ingredients
app = Flask(__name__)

# Register blueprintsapp.register_blueprint(ingredients, url_prefix="/ingredients")
@app.route('/')
def hello_world():
    return 'Hello, World!

The bolded lines are added, in essense we just import the blueprint, register it and give it a url prefix of /ingredients

Next we can test that we can hit the new endpoint by running the following using curl

[roy@localhost backend]$ curl localhost:5000/ingredients/
Hello world from ingredients

Awesome! That means our url routing it working correctly!

Additional Reading:

Configuration Management

The next step is to setup configuration management, a place to store configuration such as database connection strings, without hardcoding them inside our app. This means we can later package the app into a Docker container much easier, among other benefits.

I'm gonna use python-dotenv, .env is a popular method for storing configuration and has implemntations in a lot of different languages. To get started, lets install the package using the following command

 poetry add python-dotenv

Next lets configure dotenv in our backend/__init__.py file by adding the following, the bolded bit was added.

# backend/__init__.py
__version__ = '0.1.0'

from dotenv import load_dotenvload_dotenv()
from flask import Flask
from .modules.ingredients import ingredients

app = Flask(__name__)

# Register blueprints
app.register_blueprint(ingredients, url_prefix="/ingredients")

@app.route('/')
def hello_world():
    return 'Hello, World!'

Next we will create a dummy .env file to check that our variables are read correctly, lets start by creating a .env file in the top level of the project containing the following:

# .env
GREETING=Hello world!

python-dotenv will load the .env file into environment variables, so in the above example it will create a new environment variable called GREETING with the value "Hello world!".

Next lets reference the GREETING environment variable from our ingredients module, like so

# backend/modules/ingredients.py
from flask import Blueprint
from os import getenv

ingredients = Blueprint("ingredients", __name__)

@ingredients.route("/")
def get_ingredients():
    greeting = getenv("GREETING")    return greeting + " from ingredients"

Next we can test that we can see the greeting when we hit the endpoint using the following curl command

[roy@localhost backend]$ curl localhost:5000/ingredients/
Hello world! from ingredients

So that shows the greeting we defined in the .env file, so it looks like everything is good to go. Now we will move onto setting up a database.

Thats it for Part 1, in Part 2 we will cover setting up the SQLAlchemy to store data in a database.

Additional Reading:

© 2021, Roy Portas. Built with Gatsby