Level Up Your Flask APIs with Flask-Smorest

Isaac Tonyloi
4 min readMay 19, 2024

I’ve been diving into the world of Flask lately, and I’m impressed by how smooth and intuitive it makes building RESTful APIs. Flask’s simplicity is fantastic, but as your API grows, managing complex data structures, validation, and documentation can become cumbersome. That’s where Flask-Smorest comes in!

Flask-Smorest is a powerful extension for Flask that streamlines the development process for robust and well-structured RESTful APIs. It supercharges your Flask projects with features like:

  • Effortless Data Validation — Ensure data integrity and consistency with Marshmallow integration for data schema definition and validation.
  • Automatic API Documentation — Generate clear and comprehensive OpenAPI (Swagger) documentation for your API, making it developer-friendly.
  • Streamlined Error Handling — Handle potential errors gracefully with a clean structure and informative messages for debugging.
  • Organized Blueprints — Structure your API logic using Flask Blueprints, promoting modularity and code reusability.

1. Installation

Getting started is easy. Simply install Flask-Smorest using pip

pip install Flask-Smorest

2. Project Setup

Create a new Flask project or use an existing one. Here’s a basic Flask app structure with Flask-Smorest integration:

app.py

from flask import Flask
from flask_smorest import Api
from api.items import item_blp
from api.stores import store_blp

app = Flask(__name__)
app.config["PROPAGATE_EXCEPTIONS"] = True

api = Api(app)
api.register_blueprint(item_blp)
api.register_blueprint(store_blp)

if __name__ == "__main__":
app.run(debug=True)

api/items.py

import uuid
from flask_smorest import Blueprint, abort
from schemas.item import ItemSchema
from db import items, stores

item_blp = Blueprint("items", __name__, description="Operations on items")

@item_blp.route("/item/<string:item_id>")
class ItemResource:
@item_blp.response(200, ItemSchema)
def get(self, item_id):
try:
return items[item_id]
except KeyError:
abort(404, message="Item not found")

@item_blp.route("/item")
class ItemListResource:
@item_blp.response(200, ItemSchema(many=True))
def get(self):
return list(items.values())

@item_blp.arguments(ItemSchema)
@item_blp.response(201, ItemSchema)
def post(self, new_item_data):
if new_item_data["store_id"] not in stores:
abort(404, message="Store not found")
item_id = uuid.uuid4().hex
new_item = {**new_item_data, "id": item_id}
items[item_id] = new_item
return new_item

api/stores.py

import uuid
from flask_smorest import Blueprint, abort
from schemas.store import StoreSchema
from db import stores

store_blp = Blueprint("stores", __name__, description="Operations on stores")

@store_blp.route("/store/<string:store_id>")
class StoreResource:
@store_blp.response(200, StoreSchema)
def get(self, store_id):
try:
return stores[store_id]
except KeyError:
abort(404, message="Store not found")

@store_blp.route("/store")
class StoreListResource:
@store_blp.response(200, StoreSchema(many=True))
def get(self):
return list(stores.values())

@store_blp.arguments(StoreSchema)
@store_blp.response(201, StoreSchema)
def post(self, new_store_data):
store_id = uuid.uuid4().hex
new_store = {**new_store_data, "id": store_id}
stores[store_id] = new_store
return new_store

3. Defining Data Schemas:

With Marshmallow integration, you can define schemas to represent your API request and response data. The ItemSchema and StoreSchema are defined in separate files for clarity and reusability.

schemas/item.py

from marshmallow import Schema, fields

class ItemSchema(Schema):
id = fields.Str(dump_only=True)
name = fields.Str(required=True)
price = fields.Float(required=True)
store_id = fields.Str(required=True)

schemas/store.py

from marshmallow import Schema, fields

class StoreSchema(Schema):
id = fields.Str(dump_only=True)
name = fields.Str(required=True)

4. Building API Endpoints:

Flask-Smorest uses decorators to define API endpoints. Here’s an example of a route for creating and retrieving items.

from flask_smorest import Blueprint, abort
from flask.views import MethodView
blp = Blueprint("items", __name__, description="Operations on items")
items = []
@blp.route("/items")
class ItemsList(MethodView):
@blp.response(200, ItemSchema(many=True))
"""List all items"""
return items

@blp.arguments(ItemSchema)
@blp.response(201, ItemSchema)
def post(self, new_item):
"""Create a new item"""
new_item['id'] = len(items) + 1 # Simple ID generation
items.append(new_item)
return new_item

This example includes both a GET endpoint to list all items and a POST endpoint to create a new item. The @blp.arguments decorator validates the request body with the defined ItemSchema, and @blp.response specifies the schema for response serialization.

5. Automatic Documentation

Flask-Smorest automatically generates OpenAPI (Swagger) documentation based on your API routes and schemas. This provides developers with a clear understanding of your API’s functionalities. You can access the documentation by navigating to /swagger-ui in your browser after starting the Flask app.

6. Beyond the Basics.

Flask-Smorest offers a rich feature set to enhance your API development experience. Explore features like automatic error handling with abort, security considerations (authentication/authorization), and database integration with Flask-SQLAlchemy. For instance, here's how you can handle errors and integrate authentication:

Error Handling

Error handling is a critical aspect of any API. Flask-Smorest simplifies this by providing a straightforward way to manage errors using decorators. This allows you to define custom error responses that help clients understand what went wrong.

For instance, you can create a handler for 404 errors (Not Found) as follows

@blp.errorhandler(404)
def handle_404(error):
return {"message": str(error)}, 404

Authentication

Securing your API is essential to prevent unauthorized access and protect sensitive data. Flask-Smorest integrates seamlessly with authentication mechanisms like HTTP Basic Auth. Here’s how you can add basic authentication to your API:

First, install the necessary package:

pip install Flask-HTTPAuth

Then, set up authentication in your project:

import uuid
from flask_httpauth import HTTPBasicAuth
from flask_smorest import Blueprint, abort
from flask.views import MethodView
from schemas.item import ItemSchema
from db import items, stores
from users import verify_password

auth = HTTPBasicAuth()
auth.verify_password(verify_password)

secure_item_blp = Blueprint("secure_items", __name__, description="Operations on secure items")

@secure_item_blp.route("/secure-items")
class SecureItems(MethodView):
@auth.login_required
@secure_item_blp.response(200, ItemSchema(many=True))
def get(self):
"""List all items with authentication"""
return list(items.values())

Database Integration with Flask-SQLAlchemy

Flask-Smorest also supports seamless integration with databases using Flask-SQLAlchemy. This allows you to interact with your database through an ORM (Object-Relational Mapping) layer, simplifying data manipulation and querying.

First, install Flask-SQLAlchemy

pip install Flask-SQLAlchemy

Next, configure and use SQLAlchemy in your project

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_smorest import Api, Blueprint
from schemas.item import ItemSchema
app = Flask(__name__)

app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///mydatabase.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False

db = SQLAlchemy(app)
api = Api(app)

Conclusion

Flask-Smorest empowers you to build well-structured, maintainable, and documented RESTful APIs with Flask. With its focus on data validation, automatic documentation generation, and efficient error handling, it takes your Flask API development to the next level. So, dive into Flask-Smorest and experience the ease and power it brings to creating robust web services!

--

--

Isaac Tonyloi

Software Engineer. Fascinated by Tech and Productivity. Writing mostly for myself, sharing some of it with you