(待译)Building beautiful REST APIs using Flask, Swagger UI and Flask-RESTPlus
This article outlines steps needed to create a REST 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
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
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. Its killer feature is the ability to automatically generate interactive documentation for your API using Swagger UI.
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 software tools to generate documentation or even boilerplate code (client or server) in a variety of languages. Take a look at swagger.io for more information.
Swagger UI is a great tool for describing and visualizing RESTful web services. It generates a small webpage, which documents your API and allows you to make test queries using JavaScript. Click here to see a small demo.
In this article we’ll describe how to use Flask and Flask-RESTPlus to create a RESTful API which comes equipped with Swagger UI.
Getting started
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.
Prerequisites
You will need to have Python with Virtualenv and Git installed on your machine.
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 virtual Python environment in a directory named venv, activate the virtualenv and install required dependencies using pip:
$ virtualenv -p `which python3` venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt
Now let’s set up the app for development and start it:
(venv) $ python setup.py develop
(venv) $ python rest_api_demo/app.py
OK, 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.
from flask import Flask
from flask_restplus import Resource, Api
app = Flask(__name__) # Create a Flask WSGI application
api = Api(app) # Create a Flask-RESTPlus API
@api.route('/hello') # Create a URL route to this resource
class HelloWorld(Resource): # Create a RESTful resource
def get(self): # Create GET endpoint
return {'hello': 'world'}
if __name__ == '__main__':
app.run(debug=True) # Start a development server
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 rest_api_demo/app.py.
Take a look into the app.py file and the initialize_app function.
def initialize_app(flask_app):
configure_app(flask_app)
blueprint = Blueprint('api', __name__, url_prefix='/api')
api.init_app(blueprint)
api.add_namespace(blog_posts_namespace)
api.add_namespace(blog_categories_namespace)
flask_app.register_blueprint(blueprint)
db.init_app(flask_app)
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 api.add_namespace() function.
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).
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.
ns = api.namespace('blog/categories', description='Operations related to blog categories')
@ns.route('/')
class CategoryCollection(Resource):
def get(self):
"""Returns list of blog categories."""
return get_all_categories()
@api.response(201, 'Category successfully created.')
def post(self):
"""Creates a new blog category."""
create_category(request.json)
return None, 201
@ns.route('/int:id')
@api.response(404, 'Category not found.')
class CategoryItem(Resource):
def get(self, id):
"""Returns details of a category."""
return get_category(id)
@api.response(204, 'Category successfully updated.')
def put(self, id):
"""Updates a blog category."""
update_category(id, request.json)
return None, 204
@api.response(204, 'Category successfully deleted.')
def delete(self, id):
"""Deletes blog category."""
delete_category(id)
return None, 204