This article outlines steps needed to create a RESTful API using Flask and Flask-RESTPlus. These tools combine into a framework, which automates common tasks:
- API input validation
- formatting output (as JSON)
- generating interactive documentation (with Swagger UI)
- turning Python exceptions into machine-readable HTTP responses
Flask is a web micro-framework written in Python. Since it’s a micro-framework, Flask does very little by itself. In contrast to a framework like Django, which takes the “batteries included” approach, Flask does not come with an ORM, serializers, user management or built-in internationalization. All these features and many others are available as Flask extensions, which make up a rich, but loosely coupled ecosystem.
The challenge, then, for an aspiring Flask developer lies in picking the right extensions and combining them together to get just the right set of functions. In this article we will describe how to use the Flask-RESTPlus extension to create a Flask-based RESTful JSON API.
Flask-RESTPlus aims to make building REST APIs quick and easy. It provides just enough syntactic sugar to make your code readable and easy to maintain. The killer feature of RESTPlus is its ability to automatically generate an interactive documentation for your API using Swagger UI.
Swagger UI is part of a suite of technologies for documenting RESTful web services. Swagger has evolved into the OpenAPI specification, currently curated by the Linux Foundation. Once you have an OpenAPI description of your web service, you can use a variety of tool to generate documentation or even boilerplate code in a variety of languages. Take a look at swagger.io for more information.
In this article we’ll describe how to use Flask and Flask-RESTPlus to create a RESTful API which comes equipped with Swagger UI.
To show off the features of Flask-RESTPlus I prepared a small demo application. It’s a part of an API for a blogging platform, which allows you to manage blog posts and categories.
Let’s start by downloading and running this demo on your system, then we’ll walk through the code.
I would recommend using Python 3, but Python 2 should work just fine.
Setting up the demo application
To download and start the demo application issue the following commands. First clone the application code into any directory on your disk:
$ cd /path/to/my/workspace/ $ git clone https://github.com/postrational/rest_api_demo $ cd rest_api_demo
Create a virtualenv called
venv in the application directory, activate the virtualenv and install required dependencies using
$ pyvenv venv $ source venv/bin/activate $ pip install -r requirements.txt
Make sure the current working directory is on your
PYTHONPATH and start the app:
$ export PYTHONPATH=.:$PYTHONPATH $ python rest_api_demo/app.py
Now everything should be ready. In your browser, open the URL http://localhost:8888/api/
You should be greeted with a page similar to the following.
Defining your Flask app and RESTPlus API
Flask and Flask-RESTPlus make it very easy to get started. Minimal code required to create a working API is just 10 lines long.
1 2 3 4 5 6 7 8 9 10 11 12 13
To make the code more maintainable, in our demo application we separate the app definition, API methods and other types of code into separate files. The following directory tree shows where each part of the logic is located.
├── api # │ ├── blog # Blog-related API directory │ │ ├── business.py # │ │ ├── endpoints # API namespaces and REST methods │ │ │ ├── categories.py # │ │ │ └── posts.py # │ │ ├── parsers.py # Argument parsers │ │ └── serializers.py # Output serializers │ └── restplus.py # API bootstrap file ├── app.py # Application bootstrap file ├── database # │ └── models.py # Definition of SQLAlchemy models ├── db.sqlite # └── settings.py # Global app settings
The definition of the RESTPlus API is stored in the file
rest_api_demo/api/restplus.py, while the logic for configuring and starting the Flask app is stored in
Take a look into the
app.py file and the
1 2 3 4 5 6 7 8 9 10
This function does a number of things, but in particular it sets up a Flask blueprint, which will host the API under the
/api URL prefix. This allows you to separate the API part of your application from other parts. Your app’s frontend could be hosted in the same Flask application but under a different
blueprint (perhaps with the
/ URL prefix).
The RESTPlus API itself is also split into a number of separate namespaces. Each namespace has its own URL prefix and is stored in a separate file in the
/api/blog/endpoints directory. In order to add these namespaces to the API, we need to use the
initialize_app also sets configuration values loaded from
settings.py and configures the app to use a database through the magic of Flask-SQLAlchemy.
Defining API namespaces and RESTful resources
Your API will be organized using API namespaces, RESTful resources and HTTP methods. Namespaces, as described above, allow your API definitions to be split into multiple files, each defining a part of the API with a different URL prefix.
RESTful resources are used to organize the API into endpoints corresponding to different types of data used by your application. Each endpoint is called using a different HTTP method. Each method issues a different command to the API. For example,
GET is used to fetch a resource from the API,
PUT is used to update its information,
DELETE to delete it.
GET /blog/categories/1– Retrieve category with ID 1
PUT /blog/categories/1– Update the category with ID 1
DELTE /blog/categories/1– Delete the category with ID 1
Resources usually have an associated collection endpoint, which can be used to create new resources (
POST) or fetch lists (
GET /blog/categories– Retrieve a list of categories
POST /blog/categories– Create a new category
Using Flask-RESTPlus you can define an API for all of the endpoints listed above with the following block of code. We start by creating a namespace, we create a collection, a resource and associated HTTP methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
api.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.
@ns.route() decorator is used to specify which URLs will be associated with a given resource. You can specify path parameters using angle brackets, such as in
You can optionally specify the type of parameter using the the name of a converter and colon. Available converters are
path (string with slashes),
any (can match multiple types). The URL converters come from the Werkzeug library on which Flask is based. You can read more about them in Werkzeug docs.
Each resource is a class which contains functions which will be mapped to HTTP methods. The following functions are mapped:
If a docstring is present in any function, it will be displayed in the Swagger UI as “Implementation Notes”. You can use Markdown syntax to format these notes.
You can use the
@api.response() decorator to list what HTTP status codes each method is expected to return and what the status code means.
Once all this code is in place, your method will be nicely documented in the Swagger UI.
Swagger UI documentation also includes a form in which parameters can be set. If a request body is expected, its format will be specified on the right.
If you hit the
Try it out! button, your request will be sent to the API and the response will be displayed on screen.
Documenting and validating method parameters
We already mentioned path parameters above, but you can also document parameters in the request query (after the
? in the URL), the headers or in the form submitted in the request body.
In order to define these parameters, we use an object called the
RequestParser. The parser has a function named
add_argument(), which allows us to specify what the parameter is named and what its allowed values are.
1 2 3 4 5 6
Once defined, we can use the
@api.expect decorator to attach the parser to a method.
1 2 3 4 5 6
Once the method is decorated with an argument parser, the method’s Swagger UI will display a form to specify the argument values.
Validating argument values
The argument parser serves another function, it can validate the argument values. If a value fails validation, the API will return an
HTTP 400 error with an appropriate message.
1 2 3 4 5 6
You can enable or disable argument validation for each method using the
validate argument in the
@api.expect method. You can also enable validation globally by using the
RESTPLUS_VALIDATE configuration variable when bootstrapping your Flask application.
In the demo application we enable validation globally in the
To specify the argument’s type use the
type keyword. Allowed values are
You can specify arguments to be present in the query of your method, but also in the headers or request body using the
1 2 3
To create an argument which accepts multiple values, use the
action keyword and specify
'append' as value:
To specify a list of valid argument values, use the
choices keyword and provide an iterator as value.
Read more about RequestParser in the Flask-RESTPlus docs.
Documenting and validating request JSON objects
If you want to update or create a new resource in a RESTful collection, you should send the item’s data serialized as JSON in the body of a request. Flask-RESTPlus allows you to automatically document and validate the format of incoming JSON objects by using API models.
A RESTPlus API model defines the format of an object by listing all expected fields. Each field has an associated type (e.g.
DateTime), which determines what values will be considered valid.
The demo app has a number of API models in the
serializers.py file. A simple example would look something like this:
1 2 3 4 5 6 7 8 9 10
Once the model is defined you can attach it to a method using the
1 2 3 4 5 6 7
All fields share some common options which can change their behavior:
required– is the field required
default– default value for the field
description– field description (will appear in Swagger UI)
example– optional example value (will appear in Swagger UI)
Additional validation options can be added to fields to make them more specific:
max_length– minimum and maximum length of a string
pattern– a regular expression, which the sting must match
max– minimum and maximum values
exclusiveMax– as above, but the boundary values are not valid
multiple– number must be a multiple of this value
You can learn more about RESTPlus model fields, by looking at their source code.
Nested models and lists
A field of an API model may use another model as its expected value. You would then provide a JSON object as a valid value for this field.
A field may also require a list of values, or even a list of nested objects.
If you have two similar models, you may use model inheritance to extend the definition of a model with additional fields. In the example below we have one generic API model called
pagination and we create a more specific model
page_of_blog_posts by using the
1 2 3 4 5 6 7 8 9 10
Marshaling output JSON objects
API models can also be used as serializers. If you decorate a method with
@api.marshal_with(model), Flask-RESTPlus will generate a JSON object with the same fields as are specified in the
The method just has to return an object which has attributes with the same names as the fields. Alternatively, the method could return a dictionary with values assigned to the same keys as the names of model fields.
For example, your method can return an SQLAlchemy ORM object which has the same fields as your API model.
1 2 3 4 5 6 7 8 9 10
If you want to return a list of objects, use the
attribute keyword allows you specify which object attribute the field value should be taken from:
attribute parameter you can pull out a value nested deeper in the object’s structure:
In more complex cases you can use a
lambda function to query for the value:
When writing your API endpoint functions you may find yourself handling a request that cannot be fulfilled. In such cases your only recourse it to return an error message to the user. You can use the
api.abort() function to do so.
In cases where you don’t explicitly handle the error yourself, Flask will catch the exception and turn it into an
HTTP 500 error page.
You can override the default error handler using the
1 2 3 4 5 6 7
You can specify custom error handling logic for different types of exceptions.
1 2 3 4
default_error_handler function as written above will not return any response if the Flask application is running in
DEBUG mode. Instead of returning an error message, this will activate the Werkzeug interactive debugger.
There are a lot of resources on the net which can guide you to full Flask enlightenment. I would recommend getting to know the following: