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)
    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

    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.6

    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.

    34

    This website collects cookies to deliver better user experience

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