DEV Community

Cover image for Introducing Flask-Muck: How To Build a Comprehensive Flask REST API in 5 Minutes
Danny Tiesling
Danny Tiesling

Posted on

Introducing Flask-Muck: How To Build a Comprehensive Flask REST API in 5 Minutes

The Flask ecosystem has no shortage of great libraries for building REST APIs from the ground up. What has been missing is a tool for generating the common CRUD (create, read, update, delete) endpoints that often make up the majority of a REST API.

Flask-Muck solves this problem while coexisting with the non-standard endpoints that are inevitable in any production-scale codebase. Flask-Muck sits on top of a Flask/SQLAlchemy tech stack and uses a declarative and modular approach to replace huge amounts of boilerplate code by generating a full-featured set of standard CRUD endpoints for a resource in as little as 9 lines of code.

class MyModelApiView(FlaskMuckApiView):
    api_name = "my-model"
    session = db.session
    Model = MyModel
    ResponseSchema = MyModelSchema
    CreateSchema = MyModelSchema
    PatchSchema = MyModelSchema
    UpdateSchema = MyModelSchema
    searchable_columns = [MyModel.name]


blueprint = Blueprint("api", __name__, url_prefix="/api/v1")
MyModelApiView.add_rules_to_blueprint(blueprint)

# Available Endpoints:
# CREATE             | curl -X POST "/api/v1/my-model" -H "Content-Type: application/json" \-d "{\"name\": \"Ayla\"}"
# LIST ALL           | curl -X GET "/api/v1/my-model" -d "Accept: application/json"
# LIST ALL PAGINATED | curl -X GET "/api/v1/my-model?limit=100&offset=50" -d "Accept: application/json"
# SEARCH             | curl -X GET "/api/v1/my-model?search=ayla" -d "Accept: application/json"
# FILTER             | curl -X GET "/api/v1/my-model?filter={\"name\": \"Ayla\"}" -d "Accept: application/json"
# SORT               | curl -X GET "/api/v1/my-model?sort=name" -d "Accept: application/json"
# FETCH              | curl -X GET "/api/v1/my-model/1" -d "Accept: application/json"
# UPDATE             | curl -X PUT "/api/v1/my-model" -H "Content-Type: application/json" \-d "{\"name\": \"Ayla\"}"
# PATCH              | curl -X PATCH "/api/v1/my-model" -H "Content-Type: application/json" \-d "{\"name\": \"Ayla\"}"
# DELETE             | curl -X DELETE "/api/v1/my-model/1"
Enter fullscreen mode Exit fullscreen mode

This article guides you through building a complete Flask app that hosts a REST API for a hypothetical todo list application. Perfect for beginners in web development or those new to Flask, this walkthrough provides step-by-step instructions.

Seasoned Flask developers can skip the tutorial below and head straight to the Flask-Muck repo for links to documentation and example apps:

GitHub logo dtiesling / flask-muck

🧹 Flask REST framework for generating CRUD APIs and OpenAPI specs in the SQLAlchemy, Marshmallow/Pydantic application stack.

Code style: black CI Testing CodeQL Docs Deploy types - Mypy Static Badge License - MIT downloads pypi version All Contributors

Flask-Muck Tweet

Logo

With Flask-Muck you don't have to worry about the CRUD.

Flask-Muck is a declarative framework for automatically generating RESTful APIs with Create, Read Update and Delete (CRUD) endpoints in a Flask, SqlAlchemy, Marshmallow/Pydantic application stack in as little as 9 lines of code. Below is example code you might find in a Flask app. Please see the docs for full working examples

from flask import Blueprint, Flask
from flask_muck import FlaskMuckApiView, FlaskMuck
import marshmallow as ma
from marshmallow import fields as mf
from myapp import db


class MyModel(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String, nullable=False)


class MyModelSchema(ma.Schema):
    id = mf.Integer(dump_only=True)
    name = mf.String()


class MyModelApiView(
…
Enter fullscreen mode Exit fullscreen mode

Project Setup

Before you begin ensure you have the following prerequisites

  • Python 3.9 or higher installed
  • Bash shell or similar
  • Basic understanding of REST APIs.

1. Create Project Files

Start by creating the required file structure:

mkdir sample_api
cd sample_api
touch app.py
Enter fullscreen mode Exit fullscreen mode

Now, your project directory should have a single Python file:

sample_api/
├─ app.py
Enter fullscreen mode Exit fullscreen mode

Important: Make sure you are in the sample_api directory in your shell. The subsequent tutorial assumes this is the current working directory for all commands.

2. Install Dependencies

For a clean start with dependencies, we’ll use Pipenv. If you prefer another tool for managing environments, feel free to use it. Run the following command to install Pipenv:

pip install --user pipenv
Enter fullscreen mode Exit fullscreen mode

Next, install all the dependencies needed for the project in the virtual environment:

pipenv install flask SQLAlchemy flask-sqlalchemy marshmallow flask-muck
Enter fullscreen mode Exit fullscreen mode

Your project is set up, now you’re ready to write some code.

Write the codes

In the following section you’ll be writing all of the Python code to create the Flask REST API. All code snippets should be appended to the app.py file. For demonstration purposes module imports will be added as needed throughout the code, feel free to group them at the top of the file if you’d prefer to adhere to Pep8 style guidelines.

1. Create a Basic Flask App

In this step, we setup the most fundamental Flask app. We’re keeping it basic for this example, but you can explore more comprehensive configurations for Flask apps in numerous well-documented articles tailored for production setups.

from flask import Flask

app = Flask(__name__)
Enter fullscreen mode Exit fullscreen mode

2. Configure SqlAlchemy And Add Database Models

Earlier we installed the dependencies Flask-SQLAlchemy and SQLAlchemy.

SQLAlchemy is a SQL toolkit and ORM commonly used in conjunction with Flask to create database-backed applications. We’ll be using it to define our database tables and interact with them.

Flask-SQLAlchemy is an extension that simplifies using SQLAlchemy with Flask by setting up common objects and patterns for using those objects, such as a session tied to each web request, models, and engines.

Configure SQLAlchemy

Start by updating the app’s config with a database URI for SQLAlchemy. In this example we will point to a local sqlite file. In production Flask should be configured to use a production-ready database such as MySQL or PostgreSQL.

app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.db"
Enter fullscreen mode Exit fullscreen mode

Establish a base class for all database models by inheriting from DeclarativeBase:

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass
Create a SQLAlchemy extension instance and then initialize the Flask app for use with extension.
from flask_sqlalchemy import SQLAlchemy


db = SQLAlchemy(model_class=Base)
db.init_app(app) 
Enter fullscreen mode Exit fullscreen mode

At this point, you now have a fully functional Flask app, and the db object is ready to manage database interactions within the application.

3. Implement a Database Model

Now that we’ve configured the application, it’s time to define the database structure. Our initial step is to create a SQLAlchemy model representing a table dedicated to todo items in the database — this is where your application’s information will be persisted.

class TodoModel(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    text = db.Column(db.String, nullable=False)
Enter fullscreen mode Exit fullscreen mode

The TodoModel tracks todo items in the database. It generates a database table with two columns, id and text. id is a unique identifier for the resource and text is the description of the todo item.

4. Implement a Schema

Schemas play a crucial role in defining the structure of request and response payloads for your API. Let’s create a Marshmallow schema responsible for validating request JSON and serializing results.

import marshmallow as ma
from marshmallow import fields as mf

class TodoSchema(ma.Schema):
    id = mf.Integer(required=True, dump_only=True) 
    text = mf.String(required=True)
Enter fullscreen mode Exit fullscreen mode

Take note of the dump_only keyword argument applied to the id field. This signifies that the field exclusively serves serialization purposes for objects returned in the response. When validating requests (loading), only the text field will be validated.

For a deeper understanding of Marshmallow and schema functionality, you can explore additional details in the Marshmallow documentation.

5. Create Flask-Muck API Views

In Flask views are code components that handle http requests made to your application. Flask offers a convenient tool for organizing view code, and before we dive into creating views, let’s lay the groundwork by establishing a blueprint to streamline our API endpoints.

from flask import Blueprint

api_blueprint = Blueprint("v1_api", __name__, url_prefix="/api/v1/")
Enter fullscreen mode Exit fullscreen mode

All views added to this blueprint will be appended to the /api/v1 url route.

Now, armed with a blueprint for organized views, let’s create them. For this demonstration, we’ll leverage Flask-Muck to dynamically generate standard CRUD endpoints for our todo items. Flask-Muck offers class-based views that can be inherited from to generate CRUD endpoints.

from flask_muck import FlaskMuckApiView

class TodoApiView(FlaskMuckApiView):    
    session = db.session
    api_name = "todos"
    Model = TodoModel
    ResponseSchema = TodoSchema
    CreateSchema = TodoSchema
    PatchSchema = TodoSchema
    UpdateSchema = TodoSchema
    searchable_columns = [TodoModel.text]

# Add all url rules to the blueprint.
TodoApiView.add_rules_to_blueprint(api_blueprint)
Enter fullscreen mode Exit fullscreen mode

By defining a new FlaskMuckApiView and calling the add_rules_to_blueprint method, the following routes are added to the Flask application.

GET /api/todos/ List all todo items
POST /api/todos/ Create a todo item
GET /api/todos/ Fetch a single todo item
PUT /api/todos/ Update a single todo item
PATCH /api/todos/ Patch a single todo item
DELETE /api/todos/ Delete a single todo item

6. Define Script Behavior

In the final step we define the behavior of app.py when it is run as a script.

if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

This code orchestrates the creation of database tables and initiates the Flask development server.
The final app.py file should look similar to this:

import marshmallow as ma
from flask import Flask, Blueprint
from flask_muck import FlaskMuckApiView
from flask_sqlalchemy import SQLAlchemy
from marshmallow import fields as mf
from sqlalchemy.orm import DeclarativeBase

app = Flask(__name__)


class Base(DeclarativeBase):
    pass


db = SQLAlchemy(model_class=Base)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///todo_example.db"
db.init_app(app)


class TodoModel(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    text = db.Column(db.String, nullable=False)


class TodoSchema(ma.Schema):
    id = mf.Integer(required=True, dump_only=True)
    text = mf.String(required=True)


api_blueprint = Blueprint("v1_api", __name__, url_prefix="/api/v1/")


class TodoApiView(FlaskMuckApiView):
    session = db.session
    api_name = "todos"
    Model = TodoModel
    ResponseSchema = TodoSchema
    CreateSchema = TodoSchema
    PatchSchema = TodoSchema
    UpdateSchema = TodoSchema
    searchable_columns = [TodoModel.text]


TodoApiView.add_rules_to_blueprint(api_blueprint)
app.register_blueprint(api_blueprint)


if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)
Enter fullscreen mode Exit fullscreen mode

Interact With Your API

Now that the code is in place, let’s run the app and send some requests.

1. Start the Local Development Server

Start the local development server with the following shell command:

pipenv run python3 app.py
Enter fullscreen mode Exit fullscreen mode

Your server is now active at http://127.0.0.1:5000. Keep in mind that this configuration is not optimized for production. If you’re interested in deploying Flask apps in a production environment, numerous insightful articles cover this topic.

2. Explore the Flask-Muck API

In a new shell, begin making requests to the REST API. Experiment with these cURL commands or use your preferred REST client such as Postman.

Create a ToDo item

curl -X POST --location "http://127.0.0.1:5000/api/v1/todos/" \
    -H "Content-Type: application/json" \
    -d "{
            \"text\": \"take out garbage again\"
        }"
Enter fullscreen mode Exit fullscreen mode

List all ToDo items (flat)

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/" \
    -d "Accept: application/json"
Enter fullscreen mode Exit fullscreen mode

List all ToDo items (paginated)

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?limit=2&offset=1" \
    -d "Accept: application/json"
Enter fullscreen mode Exit fullscreen mode

Search ToDo items

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?search=garbage" \
    -d "Accept: application/json"
Enter fullscreen mode Exit fullscreen mode

Filter ToDo items

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?filters=%7B%22text%22%3A+%22take+out+garbage+again%22%7D" \
    -d "Accept: application/json"
querystring urldecodes to filters={"text": "take out garbage again"}
Enter fullscreen mode Exit fullscreen mode

Sort ToDo items

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?sort=text" \
    -d "Accept: application/json"
Enter fullscreen mode Exit fullscreen mode

Fetch ToDo item

curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/1/" \
    -d "Accept: application/json"
Enter fullscreen mode Exit fullscreen mode

Update ToDo item

curl -X PUT --location "http://127.0.0.1:5000/api/v1/todos/1/" \
    -H "Content-Type: application/json" \
    -d "{
            \"text\": \"Updated todo item\"
        }"
Enter fullscreen mode Exit fullscreen mode

Patch ToDo item

curl -X PATCH --location "http://127.0.0.1:5000/api/v1/todos/1/" \
    -H "Content-Type: application/json" \
    -d "{
            \"text\": \"Updated todo item\"
        }"
Enter fullscreen mode Exit fullscreen mode

Delete ToDo Item

curl -X DELETE --location "http://127.0.0.1:5000/api/v1/todos/1/"
Enter fullscreen mode Exit fullscreen mode

Congratulations on Building Your Functional REST API!

You've successfully built a functional REST API. Now, take the next step by exploring the Flask-Muck documentation and examples. Dig deeper into advanced features like nested resources, one-to-one endpoints, escape hatches and more.

If you enjoy the project follow it on GitHub and keep an eye on the GitHub Discussions for what’s coming next.

Top comments (0)