Flask-RESTful vs Plain Flask
You can build REST APIs in Flask in multiple styles.
Plain Flask (recommended for learning)
Section titled “Plain Flask (recommended for learning)”Advantages:
- fewer dependencies
- you learn the fundamentals
- easy to debug
A plain Flask API is just:
- routes + JSON responses
Flask-RESTful
Section titled “Flask-RESTful”Flask-RESTful provides a resource class style:
Resourceclasses- automatic parsing helpers
Pros:
- slightly more structured for large APIs
Cons:
- another abstraction layer
- not necessary for most beginner/intermediate projects
Practical recommendation
Section titled “Practical recommendation”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.
The same endpoint, both ways
Section titled “The same endpoint, both ways”@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 "", 204class 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:
flowchart TD P["plain Flask"] --> P1["one function per method"] P --> P2["errors render as HTML
unless you handle them"] P --> P3["you validate input yourself"] R["Flask-RESTful"] --> R1["one class per resource,
methods dispatch by verb"] R --> R2["errors render as JSON
by default"] R --> R3["reqparse validates and coerces"]
The difference that matters most
Section titled “The difference that matters most”Measured, requesting an item that does not exist:
| status | Content-Type | body | |
|---|---|---|---|
| plain Flask | 404 | text/html; charset=utf-8 | <!doctype html><title>404 Not Found</title>... |
| Flask-RESTful | 404 | application/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:
@app.errorhandler(404)
@app.errorhandler(500)
def json_error(e):
return jsonify(error=e.name, status=e.code), e.codeWhich is a fair summary of the whole comparison: Flask-RESTful’s advantages are all achievable in plain Flask, and it decides them for you.
Input validation
Section titled “Input validation”parser = reqparse.RequestParser()
parser.add_argument("name", type=str, required=True, help="name is required")
parser.add_argument("qty", type=int, default=0)Measured:
400 {"message": {"name": "name is required"}}201 {"received": {"name": "bolt", "qty": 7}} <- coerced to intValidation errors come back as JSON keyed by field, with no extra work.
Routing shape
Section titled “Routing shape”Measured url_map:
/items/<int:id> ['GET']
/items/<int:id> ['DELETE'] two rules for one path/items/<int:id> one rule; the class dispatches by verbWhich to choose
Section titled “Which to choose”| choose plain Flask when | choose Flask-RESTful when |
|---|---|
| the API is a handful of endpoints | many resources with uniform CRUD |
| you want no extra dependency | the class-per-resource shape fits |
| you already handle errors centrally | you want JSON errors by default |
See it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
Requesting a missing item returned 404 from both. How did the responses differ?
That is the single most practical difference for an API client. Plain Flask matches it with an errorhandler that returns jsonify.
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.
-
reqparse receives qty as the string '7' with type=int. What arrives in the parsed arguments?
Measured 201 with qty 7 as an int. A missing required field instead produced 400 with per-field JSON errors.
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.
-
What is the strongest argument against adopting Flask-RESTful today?
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.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.coffeeCtapch.feedbackHeading
pch.feedbackSubheading