Skip to content

Flask-RESTful vs Plain Flask

You can build REST APIs in Flask in multiple styles.

Advantages:

  • fewer dependencies
  • you learn the fundamentals
  • easy to debug

A plain Flask API is just:

  • routes + JSON responses

Flask-RESTful provides a resource class style:

  • Resource classes
  • automatic parsing helpers

Pros:

  • slightly more structured for large APIs

Cons:

  • another abstraction layer
  • not necessary for most beginner/intermediate projects

Use plain Flask until:

  • you feel your API structure is repeating too much

At that point you can evaluate:

  • Flask-RESTful
  • Flask-Smorest (OpenAPI)
  • or even moving to FastAPI for API-first services

For this tutorial track, we stick to plain Flask patterns so the concepts are clear.

plain_flask.py
@app.route("/items/<int:id>", methods=["GET"])
def get_item(id):
    if id not in ITEMS:
        abort(404)
    return jsonify(ITEMS[id])
 
@app.route("/items/<int:id>", methods=["DELETE"])
def del_item(id):
    ITEMS.pop(id, None)
    return "", 204
flask_restful.py
class Item(Resource):
    @marshal_with(ITEM_FIELDS)
    def get(self, id):
        if id not in ITEMS:
            abort(404)
        return ITEMS[id]
 
    def delete(self, id):
        ITEMS.pop(id, None)
        return "", 204
 
api.add_resource(Item, "/items/<int:id>")

Both return the same body for a successful GET. The differences show up at the edges:

diagram Diagram mermaid

Measured, requesting an item that does not exist:

statusContent-Typebody
plain Flask404text/html; charset=utf-8<!doctype html><title>404 Not Found</title>...
Flask-RESTful404application/json{"message": "The requested URL was not found..."}

An API that answers errors in HTML forces every client to special-case them. Plain Flask can do the same thing — it just needs saying:

json_errors.py
@app.errorhandler(404)
@app.errorhandler(500)
def json_error(e):
    return jsonify(error=e.name, status=e.code), e.code

Which is a fair summary of the whole comparison: Flask-RESTful’s advantages are all achievable in plain Flask, and it decides them for you.

reqparse.py
parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, help="name is required")
parser.add_argument("qty", type=int, default=0)

Measured:

missing a required field
400  {"message": {"name": "name is required"}}
qty sent as the string '7'
201  {"received": {"name": "bolt", "qty": 7}}      <- coerced to int

Validation errors come back as JSON keyed by field, with no extra work.

Measured url_map:

plain Flask
/items/<int:id>  ['GET']
/items/<int:id>  ['DELETE']      two rules for one path
Flask-RESTful
/items/<int:id>                  one rule; the class dispatches by verb
choose plain Flask whenchoose Flask-RESTful when
the API is a handful of endpointsmany resources with uniform CRUD
you want no extra dependencythe class-per-resource shape fits
you already handle errors centrallyyou want JSON errors by default
sketch Where the two approaches actually differ p5.js
Both serve the same successful response. The differences appear in error format, input validation and how routes are registered.
pch.quizTag pch.quizDefaultTitle
  1. Requesting a missing item returned 404 from both. How did the responses differ?

    pch.quizShowAnswer

    B — plain Flask returned an HTML error page while Flask-RESTful returned application/json — That is the single most practical difference for an API client. Plain Flask matches it with an errorhandler that returns jsonify.

  2. reqparse receives qty as the string '7' with type=int. What arrives in the parsed arguments?

    pch.quizShowAnswer

    B — the integer 7, coerced by the declared type — Measured 201 with qty 7 as an int. A missing required field instead produced 400 with per-field JSON errors.

  3. What is the strongest argument against adopting Flask-RESTful today?

    pch.quizShowAnswer

    B — everything it provides is achievable in plain Flask, while reqparse has long been documented as deprecated and releases are infrequent — Its advantages are defaults rather than capabilities. Much of this work has moved to plain Flask with pydantic, or to frameworks built for APIs.

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading