DEV Community

Carlos Villavicencio
Carlos Villavicencio

Posted on • Updated on

How to set up a REST API in Flask in 5 steps

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

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)
Enter fullscreen mode Exit fullscreen mode

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.

Swagger landing page sample

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)),
    },
)
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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"},
        ],
    }
]
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

You can fork and play with this example using this repo:

Mini example of Flask and Flask-RESTX

Actions Workflow

This is a examle repository for my article.

Setup

Create and activate the virtual environment

virtualenv venv
source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

Run the server

FLASK_ENV=development flask run
Enter fullscreen mode Exit fullscreen mode

Check out the Swagger documentation and playground at

http://localhost:5000/api/v1/

Run the tests

python -m pytest
Enter fullscreen mode Exit fullscreen mode

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

MIT

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.

Discussion (0)