Building REST APIs using Flask-RESTPlus, SQLAlchemy & Marshmallow
By: Suman Das
Tags: Flask Sqlalchemy, Flask, Python3, Flask Restful

Following are the steps required to create a sample Flask-based API for an Item & Store management application:

Setup and Installation Integrate Flask-Marshmallow Integrate Flask-SQLAlchemy Create Models Create Schemas Create Resources Application Entry Point Test the API

Prerequisites

We require Python 3 with Pipenv and Git installed. Pipenv is a package and a virtual environment manager which uses PIP under the hood. It provides more advanced features like version locking and dependency isolation between projects.

1. Setup and Installation

Once the prerequisites are in place we can begin creating the application.

a) Create a Sample Item & Store Management Flask Application

To begin with, our application, create a folder called python-sample-flask-application in any directory on the disk for our project.

$ cd /path/to/my/workspace/

$ mkdir python-sample-flask-application

$ cd python-sample-flask-application

Navigate to the project folder.

b) Activate Virtual Environment

Once we are inside the project folder, execute the following commands to activate the VirtualEnv.

pipenv shell

The virtual environment will now be activated, which will provide the required project isolation and version locking.

c) Install Dependencies

Next, install all the required dependencies using Pipenv as shown.

pipenv install flask-restplus

pipenv install flask-marshmallow

pipenv install flask-sqlalchemy

pipenv install marshmallow-sqlalchemy

After we execute the above commands, the required dependencies will be installed.

We can see now two files, which have been created inside our project folder, namely, Pipfile and Pipfile.lock .

Pipfile contains all the names of the dependencies we just installed.

contains all the names of the dependencies we just installed. Pipfile.lock is intended to specify, based on the dependencies present in Pipfile , which specific version of those should be used, avoiding the risks of automatically upgrading dependencies that depend upon each other and breaking your project dependency tree.

All the dependency versions that were installed while executing the above commands are as shown below.

flask-restplus = "==0.13.0"

flask-marshmallow = "==0.14.0"

flask-sqlalchemy = "==2.4.4"

marshmallow-sqlalchemy = "==0.24.1"

The dependency version will depend upon the latest version available at that point in time. If required, we can also update or specify the version number of any dependency in the Pipfile using the syntax flask-restplus = "==0.13.0"

d) Update ‘Werkzeug’ Dependency Version

When we install flask-restplus , werkzeug, which is a comprehensive WSGI web application library, is installed automatically with version 1.0.1 . We can locate that entry in Pipfile.lock file as follows:

"werkzeug": {

"hashes": [

"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",

"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"

],

"version": "==1.0.1"

}

However, flask-restplus is currently not compatible with extension “werkzeug=1.0.1”. Hence, we need to downgrade the version to werkzeug==0.16.1 as suggested in the Github: https://github.com/noirbizarre/flask-restplus/issues/777#issuecomment-583235327.

Accordingly, let us downgrade the version of werkzeug in Pipfile and update the installed dependencies.

[[source]]

name = "pypi"

url = "https://pypi.org/simple"

verify_ssl = true



[dev-packages]



[packages]

flask-restplus = "==0.13.0"

flask-marshmallow = "==0.14.0"

werkzeug = "==0.16.1"

flask-sqlalchemy = "==2.4.4"

marshmallow-sqlalchemy = "==0.24.1"



[requires]

python_version = "3.7"

To update the installed dependencies, execute the following command.

pipenv update

Now, we are all set to write some code for our application.

2. Integrate Flask-Marshmallow

To integrate Flask-Marshmallow with our application, create a file ma.py with the following content.

from flask_marshmallow import Marshmallow

ma = Marshmallow()

Here, we have imported Marshmallow from flask_marshmallow . Later on, we will use this Marshmallow instance ma to integrate with the flask application using the command ma.init_app(app) .

3. Integrate Flask-SQLAlchemy

For Flask-SqlAlchemy integration, create a file called db.py having the following content.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

Here, we have imported SQLAlchemy from flask_sqlalchemy . We will also use this SQLAlchemy instance db to integrate with the flask application using the command db.init_app(app) .

4. Create Database Models

Next, we will create database models for our data storage and organization. For our application, we need to create two database models Item and Store. We will be using db , the instance of the SQLAlchemy from Flask-SQLAlchemy, which we created earlier (Step 3) to create our models.

The db instance contains all the functions and helpers from both sqlalchemy and sqlalchemy.orm . It provides a class called Model that is a declarative base, which can be used to declare our models.

Create the models package and add two files named Item.py and Store.py .

4.1 Item.py

The Item.py file should contain the following content.

The above code in item.py does the following:

We started off by creating the ItemModel class in line 5 .

. In line 6 , we declared the table name items where this model will be mapped to.

, we declared the table name where this model will be mapped to. From line 8 to 10 , we defined the table columns along with their dataType.

, we defined the table columns along with their dataType. We defined the relationship with StoreModel in line 12 and 13.

From line 26 to 36 , we added some helper methods to perform search operation on items table.

to , we added some helper methods to perform search operation on table. From line 38 to 40 , we defined the save_to_db method to save the items in the database.

to , we defined the method to save the items in the database. From line 42 to 44 , we defined the delete_from_db method to delete the items from the database.

4.2 Store.py

The Store.py file should contain the following content.

Store.py

The above code in store.py does the following:

We started off by creating the StoreModel class in line 5 .

. In line 6 , we declared the table name stores where this model will be mapped to.

, we declared the table name where this model will be mapped to. From line 7 to 8 , we defined the table columns along with their dataType.

, we defined the table columns along with their dataType. We defined the relationship with ItemModel in line 10.

From line 17 to 27 , we added some helper methods to perform search operation on stores table.

to , we added some helper methods to perform search operation on table. From line 29 to 31 , we defined the save_to_db method to save the stores in the database.

to , we defined the method to save the stores in the database. From line 33 to 35 , we defined the delete_from_db method to delete the stores from the database.

The Item and Store classes inherit from db.Model class, which declares the class as a model for SQLAlchemy. The required Columns are defined in the Model class. Also, we define the relationship between Item and Store. The Store has a one-to-many relationship with Item.

Entity Diagram

5. Create Schemas

Create the Marshmallow Schemas from our models defined earlier using SQLAlchemyAutoSchema . We will be using ma , the instance of the Marshmallow from Flask-Marshmallow, which we created earlier (Step 2) to create our schemas.

Create the schemas package and add two files named Item.py and Store.py .

5.1 Item.py

The Item.py file should contain the following content.

ItemSchema.py

5.2 Store.py

The Store.py file should contain the following content.

StoreSchema.py

We need to create one schema per Model class, which we defined earlier (Step 4). Once we have defined the Schemas, we can now use these schemas to dump and load the ORM objects.

6. Create Resources

Flask-RESTPlus provides many ways to organize our application and maintain them at a granular level, such as Resources, Namespaces and Blueprints. More about this as we go along.

Resources

The main building blocks provided by Flask-RESTPlus are resources. Resources are built on top of Flask pluggable views, giving us easy access to multiple HTTP methods just by defining methods on a resource.

A resource is a class whose methods are mapped to an API/URL endpoint. It can have multiple methods, but each one must be named after one of the accepted HTTP verbs. In case, we need more than one GET or POST methods for our API, then we need to create multiple resource classes and put each method in the corresponding resource class.

Now, let’s start creating the Resources for our application. Create the resources package, and add two files named Item.py and Store.py .

6.1 Item.py

The Item.py file should contain the following content.

Item-Resource.py

In the item.py file, we have defined two resources, namely, Item and ItemList .

Item is used to perform operations on the Item table using the id field.

is used to perform operations on the table using the id field. ItemList is used to create an Item or return all the items present in the Item table.

API Model for Swagger-UI Support

Currently, Flask-RestPlus does not support ModelSchema or Model, directly, as the request body to be displayed in Swagger-UI, which is a bit of an overhead. Hence, as a workaround we can create API models to be used along with the expect annotation to achieve our goal. Flask-RESTPlus has the ability to automatically document and validate the format of incoming JSON objects by using API models. This model is only required for display in Swagger-UI.

request-body

To send or receive information (JSON objects) we can create API models. A RESTPlus API model defines the format of an object by listing all the expected fields. Each field has an associated type, such as, String , Integer , DateTime , which determines what field values will be considered valid.

In this case, we have defined an item model by specifying the information it expects and the properties of each expected value, as follows:

item = items_ns.model('Item', {

'name': fields.String('Name of the Item'),

'price': fields.Float(0.00),

'store_id': fields.Integer

})

This item model will be used as a body in Put method of Item Resource and Post method of ItemList Resource.

By using the expect annotation, for every HTTP method we can specify the expected model of the payload body to be displayed in Swagger-UI.

@item_ns.expect(item)

Namespaces

Namespaces are optional, and add a bit of additional organizational touch to the API, mainly, from a documentation point of view. A namespace allows you to group related Resources under a common root. Create the namespaces as follows:

item_ns = Namespace('item', description='Item related operations')

items_ns = Namespace('items', description='Items related operations')

Here, we created a separate Namespace for individual Resource. The Namespace() function creates a new namespace with a URL prefix. The description field will be used in the Swagger UI to describe this set of methods.

6.2 Store.py

The Store.py file should contain the following content.

Store-Resource.py

In the store.py file, we have defined two resources, namely, Store and StoreList .

Store is used to perform operations on Store table using the id field.

is used to perform operations on table using the id field. StoreList is used to create a Store or return all the stores present in the Store table.

API Model for Swagger-UI Support

Here too, we have created a store model by specifying the information it expects and the properties of each expected value, as follows:

store = stores_ns.model('Store', {

'name': fields.String('Name of the Store')

})

This store model will be used as a body in the Post method of StoreList Resource.

We will be using the expect annotation as follows:

@stores_ns.expect(store)

Namespaces

Similar to Item Resources, here also we created separate Namespaces.

store_ns = Namespace('store', description='Store related operations')

stores_ns = Namespace('stores', description='Stores related operations')

7. Application Entry Point

Now, let us create our application entry point, which brings us to blueprints.

Blueprints

Flask Blueprints helps us to organize our application by splitting common functionality into distinct components. We can organize our application components based on their function or the division of the app they cater to.

In the root directory of the project, create a file named app.py with the following content:

The above code within app.py does the following:

7.1 WSGI (Web Server Gateway Interface):

We started off by creating a Flask WSGI application in line 10 .

7.2 Flask Blueprints:

In line 11 , we created a blueprint instance by passing name , import_name and url_prefix . Flask blueprint , helps us to host the API under the /api URL prefix. This enables us to separate the API part of our application from other parts. It’s a popular way of designing modular applications.

, we created a blueprint instance by passing , and . helps us to host the API under the URL prefix. This enables us to separate the API part of our application from other parts. It’s a popular way of designing modular applications. API is the main entry point for the Flask-RESTPlus application resources. Hence, we have initialized it with the blueprint in line 12 . Here, we have overridden the default Swagger UI documentation location by passing an additional parameter doc=/doc to the initializer.

is the main entry point for the Flask-RESTPlus application resources. Hence, we have initialized it with the in . Here, we have overridden the default Swagger UI documentation location by passing an additional parameter to the initializer. The next step is to register the Blueprint with our Flask app in line 13 .

7.3 SQLAlchemy

From line 14 to 16 we have added some configuration related to SQLAlchemy. Here, we are using SQLite DB. These configurations are required to link SQLite DB with SQLAlchemy. data.db is the name of the DB File.

7.4 Namespaces

From line 18 to 21 we added all the namespaces defined earlier to the list of namespaces in the API instance. If you build the API using the namespaces composition, it becomes simpler to scale it to multiple APIs, when you have to maintain multiple versions of an API.

7.5 Database

From line 24 to 26 we have used the db instance (from file db.py ) to create the DB file along with the tables before the user accesses the server.

7.6 Exception Handling

The @api.errorhandler decorator allows us to register a specific handler for a given exception (or any exceptions inherited from it), in the same manner that we can do with Flask/Blueprint @errorhandler decorator.

7.7 Add Resources to Namespace

From line 34 to 37 we called the add_resource method on the namespaces to bring the required resources under a given namespace. We should also ensure that the name of the namespace replaces the name of the resource, so endpoints can simply refer to the URL specified as an argument. Here, for example item_ns.add_resource(Item,'/<int:id>') Item resource is available at the endpoint /item/{id} name of item_ns which is item followed by /id . We can notice the changes in the Swagger UI as follows:

Item-Swagger-UI

7.8 SQLAlchemy and Marshmallow

In line 40 and line 41 integrate SQLAlchemy and Marshmallow.

7.9 Start the Application

Finally, in line 42 we start the application at port=5000 by executing the following command.

python app.py

We are done with all the coding part and it’s testing time. Our project structure now looks as below.

Project Structure

8. Test the API

We can now test our application to ensure that everything is working fine. We can open the URL http://127.0.0.1:5000/api/doc in our browser. We should be able to see the swagger documentation as below.

Swagger-UI

Let’s test the create Store endpoint using the swagger.

Store-Create

Let’s fetch all the stores.

Fetch-Stores

As we can see in the response that there are no items in the store. So let’s start by adding some items to the stores.

Add an Item to the store.

Add-Item

Now, if we fetch the stores again, we can see the item that we added in the earlier step in the store.

fetch-stores

Similarly, we can test other operations using Swagger-UI.

If you would like to refer to the full code, do check:

https://github.com/sumanentc/python-sample-flask-application

References & Useful Readings