This is the first article in my blog, and the first article in a series that I’m planning to build, which will cover the whole process of building a Backend application and deploying it using DevOps techniques, including Continuous Integration and Continuous Deployment.

We will cover everything from Dockerizing your application, to building test pipelines and deploying it to the cloud. Notice, however, that there tutorials are not meant for absolute beginners. You must have some basic knowledge on the topics. If you feel overwhelmed, try to read the many links I’m providing throughout the posts and stepping back a bit before continuing.

So, let’s start from beginning: building a RESTful API that will then be deployed. This API can later be consumed by third party applications, or a Frontend application that you are planning to build.

Currently there are several options for someone who wants to build a RESTful API with Python. Below is a small and not at all comprehensive list containing some of those options:

  • eve - Eve is an open source package for building REST APIs. Its motto is “a framework designed for human beings”.
  • Flask-RESTful - Flask-RESTful is mature package for building RESTful applications.
  • Django REST framework - Of course we couldn’t let Django REST framework out of this list. This is probably the biggest and more overkill option that you can get for building a simple REST application, however one of the most tested and reliable options out there.
  • Falcon - Falcon is built to support a microservices based architecture, and is very fast compared to other common REST frameworks.

For this tutorial we are going to be hipsters and not use any of the above options directly, because why not? Let’s build our own solution using the vast Python ecosystem.

We will build a RESTful API using flask-apispec, which itself uses webargs for argument parsing and marshmallow for response formatting (also called response marhsalling, hence the name). It also uses apispec to automatically generate a Swagger documentation of our API. If you don’t know what Swagger is, just wait a minute and you will be amazed, but it is basically a way to document and describe your API.

This is actually a pretty good solution, since Flask-RESTful is dropping support for its own request parsers. So, using webargs and marshmallow directly is a good practice right now in the Flask world.

Getting Started

I have already set an environment for this tutorial. Go ahead and clone this repository. Then, create a virtual environment for our project, and install the requirements by running:

$ pip install -r requirements.txt

This project comes with two folders: tests and simple_rest. The tests folder contains, well… tests, while the simple_rest folder contains our actual application. This application will be very simple, defining just a user resource, and some methods to access it.

To run our application enter the simple_rest directory and run:

$ python app.py

You should then see something like:

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 127-081-590

This means that our application is running and we can access the provided link. It will of course return an error since we haven’t defined any endpoints yet.

However, we can already access the /swagger and /swagger-ui endpoints, which are generated automatically by the packages we have selected. There is nothing interesting to be found on those urls yet, though.

Let’s go through the files that compose our project, and some of their code.

simple_rest/app.py

This is the main portion of our application. It will connect everything to our Flask application.

1
2
3
4
5
6
7
app = Flask(__name__)

app.config['APISPEC_SPEC'] = APISpec(
    title='simple_rest',
    version='v1',
    plugins=('apispec.ext.marshmallow', ))
app.config['APISPEC_SWAGGER_URL'] = '/swagger/'

Here we construct our Flask object and define some configs on it. Now we are only using APISPEC-related configs, but here you would usually add, for example, some SQLAlchemy configs for your Database, among other things.

1
2
3
4
5
# Register views

# from .views import ...
# app.add_url_rule('<route>', view_func=<myview>.as_view('<viewname>'))
# ...

During this series we are going to define some endpoints for our resources. Here is where we are going to register them in the routing system of our Flask application. We will replace <route> with the desired route, <myview> with the view class, and <viewname> is the name of the view. For everything to work well, you will have to set the <viewname> as being the lowercased name of the view class <myview>. This constraint is necessary for the next step to work.

1
2
3
4
5
# Register view on apispec
docs = FlaskApiSpec(app)

# docs.register(<myview>)
# ...

Here we are registering our views on apispec. This will make the view to appear in our Swagger specification, basically. I told you in the last step that the <viewname> parameter had to be the lowercased name of the <myview> class name. This is because flask-apispec will internally search for this lowercased class name by default:

1
2
3
endpoint = endpoint or target.__name__.lower()
...
rules = self.app.url_map._rules_by_endpoint[endpoint]

(found in the flask_apispec/apidoc.py file) You can, however, use the endpoint optional argument to the register method to match the <viewname> argument.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@app.errorhandler(404)
@marshal_with(ErrorSchema, code=404)
def handle_not_found(err):
    exc = getattr(err, 'description')
    if exc:
        message = err.description
    else:
        message = ['Not found']
    return {
        'status': 'fail',
        'message': message
    }, 404

Here we are explicitly telling Flask to return a JSON response instead of the usual 404 page. This can be done for other error handlers as well. We will talk about this marshal_with thing soon.

1
2
# Enable CORS
CORS(app)

Since we are building a REST application that will potentially be accessed from different domains (i.e. not the same domain that our application is hosted on), we must enable CORS. Luckily, this is very easily done with Flask. All you have to do is use the flask-cors package and add the above line in your code!

simple_rest/schemas.py

This file will hold our response schemas. In other words, this file will have classes that describe the schema of the responses that come from our REST API.

Remember the marshal_with thing we ignored earlier? It uses those schemas to check if the response of our endpoints is correct, and if it is, it converts them to the right format.

For now we only got some empty schemas, and the error schema we saw being used earlier:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class UserSchema(Schema):
    pass

class UserResponseSchema(Schema):
    pass

class UsersResponseSchema(Schema):
    pass

class ErrorSchema(Schema):
    status = fields.String()
    message = fields.String()

This error schema is basically telling marshmallow that our 404 error handler should return a response with a status and a message, both formatted as strings.

simple_rest/store.py

This is the dumbest module present in this project. It defines a dictionary that will be used as a database. I’m doing this so that we can focus in things other than dealing with a database, which is already convoluted enough just by itself.

simple_rest/views.py

In this file I have defined two skeleton classes that will define the methods that can be used to access our user resource. These classes are the main part of this project, but they are empty for now.

simple_rest/validators.py

Despite being quite comprehensive, marshmallow fields can’t be used to validate all possible cases. Since webargs uses the same fields and validation rules as marshmallow, the same logic applies to it.

Therefore, we might need to implement some validators of our own. In this file, I have defined a validator for the user age. Basically, when someone sends some user information (payload) over to our server, we want to verify if it not smaller than 0. We could do that in our views, but it is interesting to keep views as simple as possible. Also, the validation of data is a responsibility of webargs and marshmallow, so we should keep the validation there, obeying the separation of concerns principle.

Our validator if very simple and looks like this:

1
2
3
def validate_age(age):
    if age < 0:
        raise ValidationError('Age must be greater than 0.')

tests/conftest.py

This file defines configurations for our tests. It is a good practice to keep most of our pytest fixtures in a conftest.py file.

Here I’m also giving you a good boilerplate for your Flask API tests. Simply use the client fixture to build your tests and you’re good to go.

I also define a setup_db fixture that will be run on every test, creating and cleaning the database. You might want to tune this fixture so that it runs only on specific tests that require writing on the database.

tests/test_views.py

Here we are going to build some functional tests for our views. We are not going to mock the database (even because it’s a simple dictionary) or anything. Have in mind that those tests would be considerably slower than actual unit tests.

So, you have basically two options there:

  • Mock everything.
  • Keep your views thin and simple to test and throw your logic somewhere else.

I highly recommend you follow the second path.

Summary

In this article we have covered some of the current solutions for building a REST API with Python. We focused on the solutions that are compatible with the Flask microframework.

We then reviewed the project skeleton I have created for this API. In the next post, which I plan to release next week, we are going to fill those files and create our actual application step by step.