This small tutorial was built on top of my previous code sample that configures a simple Flask app with testing:

How to add basic unit test to a Python Flask app using Pytest
Carlos V. ・ May 18 '20
There are many ways to build REST APIs, the most common is to have a Django app with DRF. Other people are trying FastAPI (I need to take a closer look at it, maybe in a future post).
But if you are using a Flask based app, I recently tried Flask-RESTX library which includes some great features:
- Swagger documentation (Heck yes!)
- Response marshalling
- Request parsing
- Error handling, logging and blueprint support. Neat Flask integration.
In this demo, I'll show you how to set up a quick REST API, with Swagger documentation, request parsing and simple response formatting.
Let's start by initializing the blueprint and defining the api object in a new module. I named this one as api.py
.
blueprint = Blueprint("api", __name__, url_prefix="/api/v1") api = Api( blueprint, version="1.0", title="Mini REST API", description="A mini REST API", ) ns = api.namespace("items", description="Item operations") api.add_namespace(ns)
Flask-RESTX support Flask Blueprint and they are really simple to implement.
My application is served at http://localhost:5000
but my API base URL will be http://localhost:5000/api/v1
. This is also the page where you can find the Swagger docs.
Next, let's write the base models. My sample API will manage Items and Details objects, so I need to write the models that will be in charge of presenting them in the API standard response.
detail_model = api.model("Detail", {"id": fields.Integer, "name": fields.String}) item_model = api.model( "Item", { "id": fields.Integer, "name": fields.String, "details": fields.List(fields.Nested(detail_model)), }, )
The idea of writing models is to use Flask-RESTX response marshalling, so no matter if our objects scale, the response will always be as we document it on our models. Flask-RESTX includes a lot of tools for this such as renaming attributes, complex, custom, and nested fields, and more.
The final set up step is to write the request parser.
item_parser = api.parser() item_parser.add_argument("id", type=int, location="form") item_parser.add_argument("name", type=str, location="form") detail_parser = api.parser() detail_parser.add_argument("id", type=int, location="form") detail_parser.add_argument("name", type=str, location="form")
In a similar way as before, we make use of Flask-RESTX request parser to read and validate values that we expect to receive in our endpoints. In this case I plan to implement two object APIs that will append elements to our database objects. (Our database is a simple in-memory object 😅)
memory_object = [ { "id": 1, "name": "Item 1", "details": [ {"id": 1, "name": "Detail 1"}, {"id": 2, "name": "Detail 2"}, ], } ]
Now it's time to implement our APIs. The first API I want to build is the one that manages the items. I will call this ItemApi
and the route will be /
which means the root of the namespace items
.
@ns.route("/") class ItemsApi(Resource): """ API for handling the Item list resource """ @api.response(HTTPStatus.OK.value, "Get the item list") @api.marshal_list_with(item_model) def get(self) -> list[Item]: """ Returns the memory object """ return memory_object @api.response(HTTPStatus.OK.value, "Object added") @api.expect(item_parser) def post(self) -> None: """ Simple append something to the memory object """ args = item_parser.parse_args() memory_object.append(args)
This will enable two endpoints:
Endpoint | Method | Parameters | Returns |
---|---|---|---|
/api/v1/items/ | GET | None | list of item_model |
/api/v1/items/ | POST | As defined on item_parser | None |
All decorators are provided by Flask-RESTX. HTTPStatus
class is provided by the http
module. Pretty simple huh?, let's build the second one.
This one will manage a single item resource. So, to get its data and add details we need the following implementation:
@ns.route("/<int:item_id>") class ItemApi(Resource): """ API for handling the single Item resource """ @api.response(HTTPStatus.OK.value, "Get the item list") @api.response(HTTPStatus.BAD_REQUEST.value, "Item not found") @api.marshal_with(item_model) def get(self, item_id: int) -> Item: """ Returns the memory object """ try: return self._lookup(item_id) except StopIteration: return api.abort(HTTPStatus.BAD_REQUEST.value, "Item not found") def _lookup(self, item_id): return next( (item for item in memory_object if item["id"] == item_id), ) @api.response(HTTPStatus.NO_CONTENT.value, "Object added") @api.response(HTTPStatus.BAD_REQUEST.value, "Item not found") @api.expect(detail_parser) def post(self, item_id: int) -> None: """ Simple append details to the memory object """ args = item_parser.parse_args() try: if item := self._lookup(item_id): item["details"].append(args) return None except StopIteration: return api.abort(HTTPStatus.BAD_REQUEST.value, "Item not found")
This will enable two more endpoints:
Endpoint | Method | Parameters | Returns |
---|---|---|---|
/api/v1/items/<item_id> | GET | None | a single item_model resource. |
/api/v1/items/<item_id> | POST | As defined on detail_parser | None |
To wrap up our application, you only need to import the module at app.py
and register the Blueprint.
from api import blueprint app = Flask(__name__) # This line already exists app.register_blueprint(blueprint)
You can fork and play with this example using this repo:
Mini example of Flask and Flask-RESTX
This is a examle repository for my article.
Setup
Create and activate the virtual environment
virtualenv venv source venv/bin/activate
Run the server
FLASK_ENV=development flask run
Check out the Swagger documentation and playground at
http://localhost:5000/api/v1/
Run the tests
python -m pytest
The server will be up on http://localhost:5000 and the API landing page will be available on http://127.0.0.1:5000/api/v1/.
Requirements
Python >= 3.9
License
I also added some unit tests and type annotations for your delight 😉.
Any feedback or suggestions are welcome and I'll be happy to answer any question you may have.
Top comments (0)