Update to REST API course, from Flask-RESTful to Flask-Smorest #42
jslvtr
announced in
Announcements
Replies: 3 comments 6 replies
-
I've moved from Flask to FastAPI |
Beta Was this translation helpful? Give feedback.
5 replies
-
my class needs help
…On Mon, Apr 25, 2022 at 10:06 AM Jose Salvatierra ***@***.***> wrote:
A lot has happened since our last update, back in July 2021. First of all,
let me apologise for the lack of activity in this course and in this repo.
We've been super busy completely re-making our Web Developer Bootcamp
course, which is now complete 🎉. This also means we're shifting our full
attention to this REST APIs with Flask and Python course 💪
Our new research into REST APIs with Flask
Over the last few months, I've been trialing the major REST libraries for
Flask. I've built REST APIs using Flask-RESTful, Flask-RESTX, and
Flask-Smorest.
I was trying to understand whether Flask-RESTful is still, in 2022 and
beyond, the best option to teach in this course.
I was looking to compare the three libraries in a few key areas:
- *Ease of use and getting started*. Many REST APIs are essentially
microservices, so being able to whip one up quickly and without having to
go through a steep learning curve is definitely interesting.
- *Maintainability and expandability*. Although many start as
microservices, sometimes we have to maintain projects for a long time. And
sometimes, they grow past what we originally envisioned.
- *Activity in the library itself*. Even if a library is suitable now,
if it is not actively maintained and improved, it may not be suitable in
the future. We'd like to teach something that you will use for years to
come.
- *Documentation and usage of best practice*. The library should help
you write better code by having strong documentation and guiding you into
following best practice. If possible, it should use existing, actively
maintained libraries as dependencies instead of implementing their own
versions of them.
- *Developer experience in production projects*. The main point here
was: how easy is it to produce API documentation with the library of
choice. Hundreds of students have asked me how to integrate Swagger in
their APIs, so it would be great if the library we teach gave it to you out
of the box.
Flask-Smorest is the most well-rounded
From our testing, we've decided to go ahead and re-make the REST APIs with
Flask and Python course by using the library Flask-Smorest.
It ticks all the boxes above:
- If you want, it can be super similar to Flask-RESTful (which is a
compliment, really easy to get started!).
- It uses marshmallow <https://marshmallow.readthedocs.io/en/stable/>
for serialization and deserialization, which is a huge plus. Marshmallow is
a very actively-maintained library which is very intuitive and unlocks very
easy argument validation. Unfortunately Flask-RESTX doesn't use
marshmallow
<https://flask-restx.readthedocs.io/en/latest/marshalling.html>,
though there are plans to do so
<python-restx/flask-restx#59>.
- It provides Swagger (with Swagger UI) and other documentations out
of the box. It uses the same marshmallow schemas you use for API validation
and some simple decorators in your code to generate the documentation.
- The documentation is the weakest point (compared to Flask-RESTX),
but with this course we can help you navigate it. The documentation of
marshmallow is superb, so that will also help.
Key differences between Flask-RESTful and Flask-Smorest
Let me tell you about some of the key differences between a project that
uses Flask-RESTful and one that uses Flask-Smorest. After reading through
these differences, it should be fairly straightforward for you to look at
two projects, each using one library, and compare them.
1. Flask-Smorest uses flask.views.MethodView classes registered under
a flask_smorest.Blueprint instead of flask_restful.Resource classes.
2. Flask-Smorest uses flask_smorest.abort to return error responses
instead of manually returning the error JSON and error code.
3. Flask-Smorest projects define marshmallow schemas that represent
incoming data (for deserialization and validation) and outgoing data (for
serialization). It uses these schemas to automatically validate the data
and turn Python objects into JSON.
MethodView and Blueprint instead of Resource classes
If you have followed the REST APIs with Flask and Python course, you'll
know that we've been defining our API endpoints like so:
from flask_restful import Resource
class Item(Resource):
def get(self, name):
pass
def post(self, name):
pass
class ItemList(Resource):
def get(self):
pass
Each of the methods in this Item class is mapped by Flask-RESTful to
Flask endpoints when we *register* the blueprint in app.py:
api.add_resource(Item, "/item/<string:name>")
api.add_resource(ItemList, "/items")
With this, we would have three endpoints:
- GET /item/<string:name> would call the Item.get method.
- POST /item/<string:name> would call the Item.post method.
- GET /items would call the ItemList.get method.
This is very similar in Flask-Smorest, with a few import changes and some
changes to how the API registration works.
First, we must define a flask_smorest.Blueprint object:
from flask.views import MethodView
from flask_smorest import Blueprint
blp = Blueprint("Items", "items", description="Operations on items")
Then we use flask.views.MethodView to define our classes, and add them to
the blueprint.
@blp.route("/item/<string:name>")
class Item(MethodView):
def get(self, name):
pass
def post(self, name):
pass
@blp.route("/items")
class ItemList(MethodView):
def get(self):
pass
To register the blueprint to the app, we'll import it and then call:
api.register_blueprint(ItemBlueprint)
I find this a bit easier, since now each segment of the application has
one blueprint rather than importing multiple different (but very closely
related) classes. Of course, you could still use a different blueprint for
each MethodView, but it is discouraged.
Using flask_smorest.abort instead of returning error JSON with a status
code
Up to now, we've been doing this when we've wanted to return an error
message:
class Item(Resource):
def get(self, name):
item = ItemModel.find_by_name(name)
if item:
return item.json()
return {"message": "Item not found"}, 404
This is well and good, but can be difficult to identify where error
messages are being returned and where success messages are being returned.
Instead, Flask-Smorest recommends doing this:
@blp.route("/item/<string:name>")
class Item(MethodView):
def get(self, name):
item = ItemModel.find_by_name(name)
if item:
return item.json()
abort(404, message="Item not found")
Define marshmallow schemas to represent incoming and outgoing data
When working with Flask-RESTful, we've gotten used to manually parsing
incoming data and validating it, using reqparse. Although this is very
clear, it is a bit limited and difficult to maintain and extend.
Similarly, to return data from our API we've been doing things such as return
item.json() or, even worse, returning a dictionary directly inline. This
is very difficult to maintain and change in the future, particularly if
doing this in many places.
Using marshmallow simplifies the flow greatly. You could use marshmallow
with Flask-RESTful, but Flask-Smorest integrates a bit better and can call
the validation for you, as well as use the schemas to generate Swagger
documentation.
Now instead of doing this:
from flask_restful import Resource, reqparse
from models import ItemModel
class Item(Resource):
parser = reqparse.RequestParser()
parser.add_argument(
"price", type=float, required=True, help="This field cannot be left blank!"
)
parser.add_argument(
"store_id", type=int, required=True, help="Every item needs a store_id."
)
def post(self, name):
data = self.parser.parse_args()
item = ItemModel(name=name, **data)
item.save_to_db()
return item.json(), 201
We can define a marshmallow model (called a *schema*) for the data inputs
and outputs. In this case, one model is enough for both, but in some cases
you may need two separate schemas:
class ItemSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True, dump_only=True)
price = fields.Float(required=True)
store_id = fields.Int(required=True, load_only=True)
store = fields.Nested(lambda: StoreWitoutItemsSchema(), dump_only=True)
Using dump_only we specify which properties are not expected as API
arguments, but will be used when we return data.
We will then import this model and use it to decorate the API endpoint
method:
from flask.views import MethodView
from flask_smorest import Blueprint
from models import ItemModel
from schemas import ItemSchema
@blp.route("/item/<string:name>")
class Item(MethodView):
@blp.arguments(ItemSchema)
@blp.response(201, ItemSchema)
def post(self, item_data, name):
item = ItemModel(**item_data, name=name)
item.save_to_db()
return item
A few things to note:
- Using the @blp.arguments decorator gives the method an extra
parameter which contains the *validated arguments*. For example, if
price or store_id were not passed in the JSON body of the API request,
this would fail with a proper error message. This is because price and
store_id are marked as required but *not* dump_only.
- Using @blp.arguments also populates the Swagger documentation and
tells the documentation reader what data the API endpoint expects and which
data points are required.
- Using the @blp.response decorator defines the default status code
(201 in this case), as well as what marshmallow model to use. When you do
this, the marshmallow model is used to turn the returned item object
into JSON. This also populates the documentation, only now the
dump_only fields would be returned and also appear in the docs.
As you can see, there is a bit more work to do initially when defining the
marshmallow schemas (and you also have to learn a bit about
marshmallow--but not too much). However, the gains are definitely worth it,
as you have to do a lot less manually and the API becomes more extensible.
The documentation generated is also very valuable.
A full migration from a Flask-RESTful project to Flask-Smorest
You may have seen some PRs appear in this project lately, and a few of
those cover how to migrate a Flask-RESTful project to Flask-RESTX and
Flask-Smorest.
You can see the PR here
<#41> for the
Flask-Smorest migration.
And that's about it! We'll be developing this course "in the open", and
you can follow this repo and my Twitter <https://twitter.com/jslvtr> for
updates. We'll be creating PRs as we introduce new content and features to
the course. We will use the develop branch for the updated course
content, and eventually merge it into master when the course is recorded.
At that point, we will also release the v2.0.0 tag.
—
Reply to this email directly, view it on GitHub
<#42>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAE6TGAHWGQRLMEWNTVFMHTVG2RFPANCNFSM5UIWGT5A>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
1 reply
-
Hi Jose, Will you be using Flask as the web server in the new course? I'm working on a production system and everything I've seen says to use a different wsgi, like Apache for production systems. Curious how Flask-Smorest works with a more secure web server. Thanks! |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
A lot has happened since our last update, back in July 2021. First of all, let me apologise for the lack of activity in this course and in this repo. We've been super busy completely re-making our Web Developer Bootcamp course, which is now complete 🎉. This also means we're shifting our full attention to this REST APIs with Flask and Python course 💪
Our new research into REST APIs with Flask
Over the last few months, I've been trialing the major REST libraries for Flask. I've built REST APIs using Flask-RESTful, Flask-RESTX, and Flask-Smorest.
I was trying to understand whether Flask-RESTful is still, in 2022 and beyond, the best option to teach in this course.
I was looking to compare the three libraries in a few key areas:
Flask-Smorest is the most well-rounded
From our testing, we've decided to go ahead and re-make the REST APIs with Flask and Python course by using the library Flask-Smorest.
It ticks all the boxes above:
Key differences between Flask-RESTful and Flask-Smorest
Let me tell you about some of the key differences between a project that uses Flask-RESTful and one that uses Flask-Smorest. After reading through these differences, it should be fairly straightforward for you to look at two projects, each using one library, and compare them.
flask.views.MethodView
classes registered under aflask_smorest.Blueprint
instead offlask_restful.Resource
classes.flask_smorest.abort
to return error responses instead of manually returning the error JSON and error code.MethodView
andBlueprint
instead ofResource
classesIf you have followed the REST APIs with Flask and Python course, you'll know that we've been defining our API endpoints like so:
Each of the methods in this
Item
class is mapped by Flask-RESTful to Flask endpoints when we register the blueprint inapp.py
:With this, we would have three endpoints:
GET /item/<string:name>
would call theItem.get
method.POST /item/<string:name>
would call theItem.post
method.GET /items
would call theItemList.get
method.This is very similar in Flask-Smorest, with a few import changes and some changes to how the API registration works.
First, we must define a
flask_smorest.Blueprint
object:Then we use
flask.views.MethodView
to define our classes, and add them to the blueprint.To register the blueprint to the app, we'll import it and then call:
I find this a bit easier, since now each segment of the application has one blueprint rather than importing multiple different (but very closely related) classes. Of course, you could still use a different blueprint for each
MethodView
, but it is discouraged.Using
flask_smorest.abort
instead of returning error JSON with a status codeUp to now, we've been doing this when we've wanted to return an error message:
This is well and good, but can be difficult to identify where error messages are being returned and where success messages are being returned.
Instead, Flask-Smorest recommends doing this:
Define marshmallow schemas to represent incoming and outgoing data
When working with Flask-RESTful, we've gotten used to manually parsing incoming data and validating it, using
reqparse
. Although this is very clear, it is a bit limited and difficult to maintain and extend.Similarly, to return data from our API we've been doing things such as
return item.json()
or, even worse, returning a dictionary directly inline. This is very difficult to maintain and change in the future, particularly if doing this in many places.Using marshmallow simplifies the flow greatly. You could use marshmallow with Flask-RESTful, but Flask-Smorest integrates a bit better and can call the validation for you, as well as use the schemas to generate Swagger documentation.
Now instead of doing this:
We can define a marshmallow model (called a schema) for the data inputs and outputs. In this case, one model is enough for both, but in some cases you may need two separate schemas:
Using
dump_only
we specify which properties are not expected as API arguments, but will be used when we return data.We will then import this model and use it to decorate the API endpoint method:
A few things to note:
@blp.arguments
decorator gives the method an extra parameter which contains the validated arguments. For example, ifprice
orstore_id
were not passed in the JSON body of the API request, this would fail with a proper error message. This is becauseprice
andstore_id
are marked asrequired
but notdump_only
.@blp.arguments
also populates the Swagger documentation and tells the documentation reader what data the API endpoint expects and which data points are required.@blp.response
decorator defines the default status code (201 in this case), as well as what marshmallow model to use. When you do this, the marshmallow model is used to turn the returneditem
object into JSON. This also populates the documentation, only now thedump_only
fields would be returned and also appear in the docs.As you can see, there is a bit more work to do initially when defining the marshmallow schemas (and you also have to learn a bit about marshmallow--but not too much). However, the gains are definitely worth it, as you have to do a lot less manually and the API becomes more extensible. The documentation generated is also very valuable.
A full migration from a Flask-RESTful project to Flask-Smorest
You may have seen some PRs appear in this project lately, and a few of those cover how to migrate a Flask-RESTful project to Flask-RESTX and Flask-Smorest.
You can see the PR here for the Flask-Smorest migration.
And that's about it! We'll be developing this course "in the open", and you can follow this repo and my Twitter for updates. We'll be creating PRs as we introduce new content and features to the course. We will use the
develop
branch for the updated course content, and eventually merge it intomaster
when the course is recorded. At that point, we will also release thev2.0.0
tag.Beta Was this translation helpful? Give feedback.
All reactions