Introducing Flask-Muck: How To Build a Comprehensive Flask REST API in 5 Minutes
Tags: python, flask, tutorial, opensource
Published: 2023-12-20 15:54:34

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.

```python
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"
```

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:

{% embed https://github.com/dtiesling/flask-muck %}

## 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:

```bash
mkdir sample_api
cd sample_api
touch app.py
```

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

```
sample_api/
├─ app.py
```

**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:

```bash
pip install --user pipenv
```

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

```bash
pipenv install flask SQLAlchemy flask-sqlalchemy marshmallow flask-muck
```

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.

```python
from flask import Flask

app = Flask(__name__)
```

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

```python
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.db"
```

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

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

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.

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

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.

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

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.

```python
from flask import Blueprint

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

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.

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

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/<ID>** Fetch a single todo item
**PUT /api/todos/<ID>** Update a single todo item
**PATCH /api/todos/<ID>** Patch a single todo item
**DELETE /api/todos/<ID>** 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.

```python
if __name__ == "__main__":
    with app.app_context():
        db.create_all()
    app.run(debug=True)
```

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

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

## 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:

```bash
pipenv run python3 app.py
```

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

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

**List all ToDo items (flat)**

```bash
curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/" \
    -d "Accept: application/json"
```

**List all ToDo items (paginated)**

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

**Search ToDo items**

```bash
curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?search=garbage" \
    -d "Accept: application/json"
```

**Filter ToDo items**

```bash
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"}
```

**Sort ToDo items**

```bash
curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/?sort=text" \
    -d "Accept: application/json"
```

**Fetch ToDo item**

```bash
curl -X GET --location "http://127.0.0.1:5000/api/v1/todos/1/" \
    -d "Accept: application/json"
```

**Update ToDo item**

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

**Patch ToDo item**

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

**Delete ToDo Item**

```bash
curl -X DELETE --location "http://127.0.0.1:5000/api/v1/todos/1/"
```

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