Structuring a Flask-Restful API for Production

Welcome to my Flask REST API tutorial, so the goal of this tutorial is to introduce you to writing APIs with Flask.

We would be building a simple personal diary API with these basic features.

  • Add notes with a date and text
  • Show more recent note first
  • Attach images to your diary

 

WHAT YOU WILL LEARN FROM THIS TUTORIAL.

  1.  Structuring a Flask Restful project for production.
  2. Basic CRUD operations(GET, POST, UPDATE, and DELETE).
  3. Authentication with JWT 
  4. Object serialization with Marshmallow
  5. Email setup and confirmation
  6. Working with file upload
  7. Adding Custom Error messages and handlers
  8. Logging
  9. Running Tests
  10. Deployment

This new post would be in series and we would begin with structuring our Flask Rest project for production.

Structuring a Flask Restful project for production.

Unlike most frameworks, Flask does not impose a specific organization. For this tutorial, we would structure our app like the image below so it scales pretty well.

flask_rest

So we are going to install the following dependencies for our diary API.

1. Flask: web microframework for Python
2. Flask-restful: extension for Flask that adds support for building APIs.
3. Flask-CORS: is a flask extension for handling Cross-Origin Resource Sharing (CORS) making cross-origin AJAX possible.
4. Flasgger: an extension that creates Swagger 2.0 API documentation for all your Flask views.

Let’s Build our Project

It’s a good practice for python projects to run on their own specified independent virtual environments.

1. Create our virtual environment and install our app dependencies.

From the terminal do the following (Mac and Linux Users)

mkdir recipe-api & cd diary_api
python 3 -m venv env
source env/bin/activate
pip install -r requirements.txt

Window Users

py -m venv env
 .\env\Scripts\activate 
 pip install -r requirements.txt

ALTERNATIVELY, if you use Pyenv like I do(a simple Python management tool that allows  you to easily switch between multiple versions of Python. )

pyenv update                           //update pyenv  
pyenv install --list                  //To see available versions of python
pyenv install -v 3.8.3               //install latest version of python 
pyenv virtualenv 3.8.3 env          //select our python version and create 
                                    // virtual environment 
pyenv local env                    //activate our environment 
pip install -r requirements.txt  
pyenv deactivate                  // remember to deactivate

2.  Setting Up Configuration

Create a new folder, called api , create an __init__.py , app.py, and config.py file. Add the following code below to the config.py file

#api/config.py

import os

basedir = os.path.abspath(os.path.dirname(__file__))



class Config:
    SECRET_KEY = os.environ.get("SECRET_KEY")
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    
    @staticmethod
    def init_app(app):
        pass


class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "DEV_DATABASE_URL"
    ) or "sqlite:///" + os.path.join(basedir, "dev.sqlite")


class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "TEST_DATABASE_URL"
    ) or "sqlite:///" + os.path.join(basedir, "test.sqlite")


class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get(
        "DATABASE_URL"
    ) or "sqlite:///" + os.path.join(basedir, "prod.sqlite")


env_config = {
    "development": DevelopmentConfig,
    "testing": TestingConfig,
    "production": ProductionConfig,
}

The Config is our base class that contains settings that are common to all configurations and it implements an empty init_app() method.
The SQLALCHEMY_DATABASE_URI variable is assigned different values under each of the three configurations. This enables the application to use a different database in each configuration. This is very important, as you don’t want a run of the unit tests to the database that you use for day-to-day development.
At the bottom of the config.py, you notice a dictionary (env_config), contains config for the various environment (development, testing, or development).

3. Define the application factory
Add the code below to the app.py file

#api/app.py
from flask import Flask
from flask_cors import CORS
from flask_restful import Api
from flasgger import Swagger

from api.config import env_config

api = Api()


def create_app(config_name):
    #import our resource folder to avoid circular 
    #dependency error
    import resources

    app = Flask(__name__)

    app.config.from_object(env_config[config_name])
    api.init_app(app)
    
    CORS(app)
    Swagger(app)
    
    return app

 

The create_app() function is the application factory, which takes as an argument the name of a configuration to use for the application. The configuration settings stored in one of the classes defined in config.py can be imported directly into the application using the  from_object()
method.

4. Define the application instance (the flask app)

Create a new file in the root folder called main.py

#main.py

import os

from api.app import create_app

app = create_app(os.getenv("FLASK_ENV"))

5. Test and create our default resource

Create a new folder called resources, create the following files (__init__.py and default.py).

Now add the following to default.py file

from flask_restful import Resource

from api.app import api


class DefaultResource(Resource):
    """Handle default route."""

    def get(self):
        """Get request for home page or response."""
        return {
            "status": "success",
            "data": {
                "msg": "Welcome to our diary API"
            }
        }

api.add_resource(DefaultResource, "/", endpoint="home")

Import the default module into resources/__init__.py to avoid circular dependency error.

#resources/__init__.py

from . import default

Now run the following on the terminal to start our app.

export FLASK_APP=main.py
export FLASK_DEBUG=1
export FLASK_ENV=development
flask run

For Window users, run the following

set FLASK_APP=main.py
set FLASK_DEBUG=1
set FLASK_ENV=development
flask run

This following below will be displayed on your terminal, press your Ctrl key, and the http://127.0.0.1:5000/  to go to your browser.

:~/Desktop/blog_Notes/diary_app$ flask run

 * Serving Flask app "main.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 326-455-266
127.0.0.1 - - [09/Jul/2020 19:30:32] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [09/Jul/2020 19:31:12] "GET /apidocs/ HTTP/1.1" 200 -

Viola!

Our API is up and running.

localhost

Open a new tab on your browser and type http://127.0.0.1:5000/apidocs/,  our Swagger is up and running.

swagger

In my next post, we would create custom error handlers and handle exceptions in our Flask app.

Thanks for visiting my blog, here is a Github repo for the post, click here

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *