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.
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.
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 😅)
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("/")classItemsApi(Resource):"""
API for handling the Item list resource
"""@api.response(HTTPStatus.OK.value,"Get the item list")@api.marshal_list_with(item_model)defget(self)->list[Item]:"""
Returns the memory object
"""returnmemory_object@api.response(HTTPStatus.OK.value,"Object added")@api.expect(item_parser)defpost(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>")classItemApi(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)defget(self,item_id:int)->Item:"""
Returns the memory object
"""try:returnself._lookup(item_id)exceptStopIteration:returnapi.abort(HTTPStatus.BAD_REQUEST.value,"Item not found")def_lookup(self,item_id):returnnext((itemforiteminmemory_objectifitem["id"]==item_id),)@api.response(HTTPStatus.NO_CONTENT.value,"Object added")@api.response(HTTPStatus.BAD_REQUEST.value,"Item not found")@api.expect(detail_parser)defpost(self,item_id:int)->None:"""
Simple append details to the memory object
"""args=item_parser.parse_args()try:ifitem:=self._lookup(item_id):item["details"].append(args)returnNoneexceptStopIteration:returnapi.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.
fromapiimportblueprintapp=Flask(__name__)# This line already exists
app.register_blueprint(blueprint)
You can fork and play with this example using this repo: