Basic Usage¶
This chapter will cover the primary usage of APIFlask.
Prerequisites¶
- Python 3.7+
- Flask 1.1+
You also need to know the basic of Flask. Here are some useful free resources to learn Flask:
Installation¶
$ pip3 install apiflask
> pip install apiflask
Python dependency management tools
The command above use pip to install APIFlask, you can also use other dependencies management tools such as Poetry, Pipenv, PDM, etc.
Create an app
instance with APIFlask
class¶
Similar to what you did to create a Flask app
instance, you will need to import
APIFlask
class from apiflask
package, then create the app
instance from
the APIFlask
class:
from apiflask import APIFlask
app = APIFlask(__name__)
@app.get('/')
def index():
return {'message': 'hello'}
The default title and version of the API will be APIFlask
and 0.1.0
; you can
pass the title
and the version
arguments to change these settings:
app = APIFlask(__name__, title='Wonderful API', version='1.0')
To run this application, you can save it as app.py
, then run the flask run
command:
$ flask run
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
If your script's name isn't app.py
, you will need to declare which application
should be started before execute flask run
. See the note below for more details.
Assign the specific application to run
In default, Flask will look for an application instance called app
or application
or application factory function called create_app
or make_app
in module/package
called app
or wsgi
. That's why I recommend naming the file as app.py
. If you
use a different name, then you need to tell Flask the application module path via the
--app
(Flask 2.2+) option or the environment variable FLASK_APP
. For example, if
your application instance stored in a file called hello.py
, then you will need to
set --app
or FLASK_APP
to the module name hello
:
$ flask --app hello run
or:
$ export FLASK_APP=hello
> set FLASK_APP=hello
> $env:FLASK_APP="hello"
Similarly, If your application instance or application factory function stored in
mypkg/__init__.py
, you can pass the package name:
$ flask --app mypkg run
$ export FLASK_APP=mypkg
> set FLASK_APP=mypkg
> $env:FLASK_APP="mypkg"
However, if the application instance or application factory function store in
mypkg/myapp.py
, you will need to use:
$ flask --app mypkg.myapp run
or:
$ export FLASK_APP=mypkg.myapp
> set FLASK_APP=mypkg.myapp
> $env:FLASK_APP="mypkg.myapp"
See Application Discovery for more details.
If you want to make the application restart whenever the code changes, you can enable
reloader with --reload
option:
$ flask run --reload
Tip
Install watchdog
for a better performance for the application reloader:
$ pip3 install watchdog
> pip install watchdog
We highly recommend enabling "debug mode" when developing Flask application. See the note below for the details.
Enable the debug mode
Flask can automatically restart and reload the application when code changes
and display useful debug information for errors. To enable these features
in your Flask application, we will need to use the --debug
option:
$ flask --debug run
If you are not using the latest Flask version (>2.2), you will need to set
the environment variable FLASK_DEBUG
to True
instead:
$ export FLASK_DEBUG=True
> set FLASK_DEBUG=True
> $env:FLASK_DEBUG="True"
See Debug Mode for more details.
Manage environment variables with python-dotenv¶
Manually setting environment is a bit inconvenient since the variable only lives in the current terminal session. You have to set it every time you reopen the terminal or reboot the computer. That's why we need to use python-dotenv, and Flask also has special support for it.
Install python-dotenv
with pip:
$ pip3 install python-dotenv
> pip install python-dotenv
Now we can store environment variables in .env files. Flask-related environment
variables should keep in a file called .flaskenv
:
# save as .flaskenv
FLASK_APP=hello
FLASK_ENV=development
While the secrets values should save in the .env
file:
# save as .env
SECRET_KEY=some-random-string
DATABASE_URL=your-database-url
FOO_APP_KEY=some-app-key
Warning
Since the .env
contains sensitive information, do not commit it into the
Git history. Be sure to ignore it by adding the file name into .gitignore
.
In the application, now we can read these variables via os.getenv(key, default_value)
:
import os
from apiflask import APIFlask
app = APIFlask(__name__)
app.secret_key = os.getenv('SECRET_KEY')
Any flask
command will read environment variables set by .flaskenv
and .env
.
Now when you run flask run
, Flask will read the value of FLASK_APP
and FLASK_ENV
in .flaskenv
file to find the app instance from given import path and enable the
debug mode:
$ flask run
* Environment: development
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 101-750-099
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
See Environment Variables From dotenv for more details.
Interactive API documentation¶
Once you have created the app instance, the interactive API documentation will be available at http://localhost:5000/docs. On top of that, the OpenAPI spec file is available at http://localhost:5000/openapi.json.
If you want to preview the spec or save the spec to a local file, use the flask spec
command.
You can refresh the documentation whenever you added a new route or added the input and output definition for the view function in the following sections.
Read the API Documentations chapter for the advanced topics on API docs.
Create a route with route decorators¶
To create a view function, you can do exactly what you did with Flask:
from apiflask import APIFlask
app = APIFlask(__name__)
@app.route('/')
def index():
return {'message': 'hello'}
@app.route('/pets/<int:pet_id>')
def get_pet(pet_id):
return {'message': 'OK'}
@app.route('/pets')
def get_pets():
return {'message': 'OK'}
@app.route('/pets', methods=['POST'])
def create_pet():
return {'message': 'created'}, 201
@app.route('/pets/<int:pet_id>', methods=['PUT'])
def update_pet(pet_id):
return {'name': 'updated'}
@app.route('/pets/<int:pet_id>', methods=['DELETE'])
def delete_pet(pet_id):
return '', 204
However, with APIFlask, instead of setting methods
argument for each route, you can
also use the following shortcuts decorators:
app.get()
: register a route that only accepts GET request.app.post()
: register a route that only accepts POST request.app.put()
: register a route that only accepts PUT request.app.patch()
: register a route that only accepts PATCH request.app.delete()
: register a route that only accepts DELETE request.
Here is the same example with the route shortcuts:
from apiflask import APIFlask
app = APIFlask(__name__)
@app.get('/')
def index():
return {'message': 'hello'}
@app.get('/pets/<int:pet_id>')
def get_pet(pet_id):
return {'message': 'OK'}
@app.get('/pets')
def get_pets():
return {'message': 'OK'}
@app.post('/pets')
def create_pet():
return {'message': 'created'}, 201
@app.put('/pets/<int:pet_id>')
def update_pet(pet_id):
return {'message': 'updated'}
@app.delete('/pets/<int:pet_id>')
def delete_pet(pet_id):
return '', 204
Handling multiple HTTP methods in one view function
You can't pass the methods
argument to route shortcuts. If you want the
view function to accept multiple HTTP methods, you will need to use the
app.route()
decorator to pass the methods
argument:
@app.route('/', methods=['GET', 'POST'])
def index():
return {'message': 'hello'}
Or you can do something like this:
@app.get('/')
@app.post('/')
def index():
return {'message': 'hello'}
By the way, you can mix the use of app.route()
with the shortcuts in your
application.
Move to new API decorators¶
From APIFlask 0.12, the four standalone API decorators (i.e. @input
, @output
,
@doc
, and @auth_required
) were moved to APIFlask
and APIBlueprint
classes.
Now access them with your application or blueprint instance:
from apiflask import APIFlask
app = APIFlask(__name__)
@app.get('/')
@app.input(Foo)
@app.output(Bar)
def hello():
return {'message': 'Hello'}
instead of:
from apiflask import APIFlask, input, output
app = APIFlask(__name__)
@app.get('/')
@input(Foo)
@output(Bar)
def hello():
return {'message': 'Hello'}
The old standalone decorators were deprecated since 0.12, and will be removed in the 1.0 version. Notice all the usage in the docs are updated, you may want to upgrade APIFlask to update the usage.
Use @app.input
to validate and deserialize request data¶
To validate and deserialize a request body or request query parameters, we need to create a data schema class first. Think of it as a way to describe the valid incoming data. If you already familiar with marshmallow, then you already know how to write a data schema.
Here is a simple input schema for a Pet input resource:
from apiflask import Schema
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf
class PetIn(Schema):
name = String(required=True, validate=Length(0, 10))
category = String(required=True, validate=OneOf(['dog', 'cat']))
Tip
See Schema and Fields chapter (WIP) for the details of how to write a schema and the examples for all the fields and validators.
A schema class should inherit the apiflask.Schema
class:
from apiflask import Schema
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf
class PetIn(Schema):
name = String(required=True, validate=Length(0, 10))
category = String(required=True, validate=OneOf(['dog', 'cat']))
fields are represented with field classes in apiflask.fields
:
from apiflask import Schema
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf
class PetIn(Schema):
name = String(required=True, validate=Length(0, 10))
category = String(required=True, validate=OneOf(['dog', 'cat']))
To validate a field with a specific rule, you can pass a validator or a list of
validators (import them from apiflask.validators
) to the validate
argument
of the field class:
from apiflask import Schema
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf
class PetIn(Schema):
name = String(required=True, validate=Length(0, 10))
category = String(required=True, validate=OneOf(['dog', 'cat']))
Tip
Notice we mark the field as a required field with the required
parameter.
If you want to set a default value for an input field when is missing in
the input data, you can use the load_default
parameter:
name = String(load_default='default name')
With this schema, we declare that the input request body should appear in the following format:
{
"name": "the name of the pet",
"category": "the category of the pet: one of dog and cat"
}
Notes
Read the Data Schema chapter for the advanced topics on data schema.
Now let's add it to the view function which used to create a new pet:
from apiflask import APIFlask, Schema, input
from apiflask.fields import Integer, String
from apiflask.validators import Length, OneOf
app = APIFlask(__name__)
class PetIn(Schema):
name = String(required=True, validate=Length(0, 10))
category = String(required=True, validate=OneOf(['dog', 'cat']))
@app.post('/pets')
@app.input(PetIn)
def create_pet(data):
print(data)
return {'message': 'created'}, 201
You just need to pass the schema class to the @app.input
decorator. When a request
was received, APIFlask will validate the request body against the schema.
If the validation passed, the data will inject into the view function as
a positional argument in the form of dict
. Otherwise, an error response
with the detail of the validation result will be returned.
In the example above, I use the name data
to accept the input data dict.
You can change the argument name to whatever you like. Since this is a dict,
you can do something like this to create an ORM model instance:
@app.post('/pets')
@app.input(PetIn)
@app.output(PetOut)
def create_pet(pet_id, data):
pet = Pet(**data)
return pet
or update an ORM model class instance like this:
@app.patch('/pets/<int:pet_id>')
@app.input(PetIn)
@app.output(PetOut)
def update_pet(pet_id, data):
pet = Pet.query.get(pet_id)
for attr, value in data.items():
setattr(pet, attr, value)
return pet
If you want to mark the input with a different location, you can pass a location
argument for @app.input()
decorator, the value can be:
- Request JSON body:
'json'
(default) - Upload files:
'files'
- Form data:
'form'
- Form data and files:
'form_and_files'
- Cookies:
'cookies'
- HTTP headers:
'headers'
- Query string:
'query'
(same as'querystring'
) - Path variable (URL variable):
'path'
(same as'view_args'
, added in APIFlask 1.0.2)
Warning
Be sure to put the @app.input
decorator under the routes decorators
(i.e., app.route
, app.get
, app.post
, etc.).
Read the Request Handling chapter for the advanced topics on request handling.
Use @app.output
to format response data¶
Similarly, we can define a schema for output data with @app.output
decorator. Here is an example:
from apiflask.fields import String, Integer
class PetOut(Schema):
id = Integer()
name = String()
category = String()
Since APIFlask will not validate the output data, we only need to list all the field for the output schema.
Tip
You can set a default value for output field with the dump_default
argument:
name = String(dump_default='default name')
Now add it to the view function which used to get a pet resource:
from apiflask import APIFlask, output
from apiflask.fields import String, Integer
app = APIFlask(__name__)
class PetOut(Schema):
id = Integer()
name = String()
category = String()
@app.get('/pets/<int:pet_id>')
@app.output(PetOut)
def get_pet(pet_id):
return {
'name': 'Coco',
'category': 'dog'
}
The default status code for output response is 200
, you can set a different
status code with the status_code
argument:
@app.post('/pets')
@app.input(PetIn)
@app.output(PetOut, status_code=201)
def create_pet(data):
data['id'] = 2
return data
Or just:
@app.output(PetOut, status_code=201)
If you want to return a 204 response, you can use the EmptySchema
from apiflask.schemas
:
from apiflask.schemas import EmptySchema
@app.delete('/pets/<int:pet_id>')
@app.output(EmptySchema, status_code=204)
def delete_pet(pet_id):
return ''
From version 0.4.0, you can use a empty dict to represent empty schema:
@app.delete('/pets/<int:pet_id>')
@app.output({}, status_code=204)
def delete_pet(pet_id):
return ''
The @app.output
decorator can only use once
You can only define one main success response for your view function,
which means you can only use one @app.output
decorator. If you want to
add more alternative responses for a view in the OpenAPI spec, you can
use the @app.doc
decorator and pass a list to the responses
parameter.
For example:
@app.put('/pets/<int:pet_id>')
@app.input(PetIn)
@app.output(PetOut) # 200
@app.doc(responses=[204, 404])
def update_pet(pet_id, data):
pass
Warning
Be sure to put the @app.output
decorator under the routes decorators
(i.e., app.route
, app.get
, app.post
, etc.).
Read the Response Formatting chapter for the advanced topics on request formatting.
The return value of the view function¶
When you are using a @app.output(schema)
decorator, you should return a dict or object
that matches the schema you passed. For example, here is your schema:
from apiflask import Schema
from apiflask.fields import String, Integer
class PetOut(Schema):
id = Integer()
name = String()
category = String()
Now you can return a dict:
@app.get('/pets/<int:pet_id>')
@app.output(PetOut)
def get_pet(pet_id):
return {
'id': 1,
'name': 'Coco',
'category': 'dog'
}
or you can return an ORM model instance directly:
@app.get('/pets/<int:pet_id>')
@app.output(PetOut)
def get_pet(pet_id):
pet = Pet.query.get(pet_id)
return pet
Notice your ORM model class should have the fields defined in the schema class:
class Pet(Model):
id = Integer()
name = String()
category = String()
What if I want to use a different external field name?
For example, in your ORM model class, you have a phone
field that
store the user's phone number:
class User(Model):
phone = String()
Now you want to output the field with the name phone_number
, then you can use
data_key
to declare the actual key name to dump to:
class UserOut(Schema):
phone = String(data_key='phone_number')
This schema will generate something like {'phone_number': ...}
.
Similarly, you can tell APIFlask to load from different key in input schema:
class UserIn(Schema):
phone = String(data_key='phone_number')
This schema expects the user input is something like {'phone_number': ...}
.
The default status code is 200
, if you want to use a different status code,
you can pass a status_code
argument in the @app.output
decorator:
@app.post('/pets')
@app.input(PetIn)
@app.output(PetOut, status_code=201)
def create_pet(data):
# ...
return pet
You don't need to return the same status code in the end of the view function
(i.e., return data, 201
):
@app.post('/pets')
@app.input(PetIn)
@app.output(PetOut, status_code=201)
def create_pet(data):
# ...
# equals to:
# return pet, 201
return pet
When you want to pass a header dict, you can pass the dict as the second element of the return tuple:
@app.post('/pets')
@app.input(PetIn)
@app.output(PetOut, status_code=201)
def create_pet(data):
# ...
# equals to:
# return pet, 201, {'FOO': 'bar'}
return pet, {'FOO': 'bar'}
Tip
Be sure to always set the status_code
argument in @app.output
when you want
to use a non-200 status code. If there is a mismatch, the status_code
passed in @app.output
will be used in OpenAPI spec, while the actual response
will use the status code you returned at the end of the view function.
The OpenAPI generating support and the @app.doc
decorator¶
APIFlask provides automatic OpenAPI spec generating support, while also allows you to customize the spec:
- Most of the fields of the
info
object and top-level field ofOpenAPI
objct are accessible with configuration variables. - The
tag
object, Operationsummary
anddescription
will generated from the blueprint name, the view function name and docstring. - You can register a spec processor function to process the spec.
requestBody
andresponses
fields can be set with theinput
andoutput
decorator.- Other operation fields can be set with the
doc
decorator:
from apiflask import APIFlask, doc
app = APIFlask(__name__)
@app.get('/hello')
@app.doc(summary='Say hello', description='Some description for the /hello')
def hello():
return 'Hello'
See Use the doc
decorator for more details
about OpenAPI genenrating and the usage of the doc
decorator.
Warning
Be sure to put the @app.doc
decorator under the routes decorators
(i.e., app.route
, app.get
, app.post
, etc.).
Use @app.auth_required
to protect your views¶
Based on Flask-HTTPAuth, APIFlask provides three types of authentication:
HTTP Basic¶
To implement an HTTP Basic authentication, you will need to:
- Create an
auth
object withHTTPBasicAuth
- Register a callback function with
@auth.verify_password
, the function should acceptusername
andpassword
, return the corresponding user object orNone
. - Protect the view function with
@app.auth_required(auth)
. - Access the current user object in your view function with
auth.current_user
.
from apiflask import APIFlask, HTTPBasicAuth
app = APIFlask(__name__)
auth = HTTPBasicAuth() # create the auth object
@auth.verify_password
def verify_password(username, password):
# get the user from the database, check the password
# then return the user if the password matches
# ...
@app.route('/')
@app.auth_required(auth)
def hello():
return f'Hello, {auth.current_user}!'
HTTP Bearer¶
To implement an HTTP Bearer authentication, you will need to:
- Create an
auth
object withHTTPTokenAuth
- Register a callback function with
@auth.verify_password
, the function should accepttoken
, return the corresponding user object orNone
. - Protect the view function with
@app.auth_required(auth)
. - Access the current user object in your view function with
auth.current_user
.
from apiflask import APIFlask, HTTPTokenAuth
app = APIFlask(__name__)
auth = HTTPTokenAuth() # create the auth object
# or HTTPTokenAuth(scheme='Bearer')
@auth.verify_token # register a callback to verify the token
def verify_token(token):
# verify the token and get the user id
# then query and return the corresponding user from the database
# ...
@app.get('/')
@app.auth_required(auth) # protect the view
def hello():
# access the current user with auth.current_user
return f'Hello, {auth.current_user}'!
API Keys (in header)¶
Similar to the Bearer type, but set the scheme
to ApiKey
when creating the
auth object:
from apiflask import HTTPTokenAuth
HTTPTokenAuth(scheme='ApiKey')
or with a custom header:
from apiflask import HTTPTokenAuth
HTTPTokenAuth(scheme='ApiKey', header='X-API-Key')
# ...
You can set the OpenAPI security description with the description
parameter
in HTTPBasicAuth
and HTTPTokenAuth
.
See Flask-HTTPAuth's documentation to learn
the details. However, remember to
import HTTPBasicAuth
and HTTPTokenAuth
from APIFlask and use @app.auth_required
instead of @auth.login_required
for your view functions.
Warning
Be sure to put the @app.auth_required
decorator under the routes decorators
(i.e., app.route
, app.get
, app.post
, etc.).
Read the Authentication chapter for the advanced topics on authentication.
Use class-based views¶
Version >= 0.5.0
This feature was added in the version 0.5.0.
You can create a group of routes under the same URL rule with the MethodView
class.
Here is a simple example:
from flask.views import MethodView
from apiflask import APIFlask
app = APIFlask(__name__)
class Pet(MethodView):
def get(self, pet_id):
return {'message': 'OK'}
def delete(self, pet_id):
return '', 204
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
When creating a view class, it needs to inherit from the MethodView
class, since APIFlask
can only generate OpenAPI spec for MethodView
-based view classes.
Now, you can define view methods for each HTTP method, use the (HTTP) method name as method name:
class Pet(MethodView):
def get(self, pet_id): # triggered by GET request
return {'message': 'OK'}
def post(self, pet_id): # triggered by POST request
return {'message': 'OK'}
def put(self, pet_id): # triggered by PUT request
return {'message': 'OK'}
def delete(self, pet_id): # triggered by DELETE request
return '', 204
def patch(self, pet_id): # triggered by PATCH request
return {'message': 'OK'}
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
With the example application above, when the user sends a GET request to
/pets/<int:pet_id>
, the get()
method of the Pet
class will be called,
and so on for the others.
Normally you don't need to specify the methods, unless you want to register
multiple rules for one single view classe. For example, register the post
method
to a different URL rule than the others:
class Pet(MethodView):
# ...
pet_view = Pet.as_view('pet')
app.add_url_rule('/pets/<int:pet_id>', view_func=pet_view, methods=['GET', 'PUT', 'DELETE', 'PATCH'])
app.add_url_rule('/pets', view_func=pet_view, methods=['POST'])
However, you may want to create separate classes for different URL rules.
When you use decorators like @app.input
, @app.output
, be sure to use it on method
instead of class:
class Pet(MethodView):
@app.output(PetOut)
@app.doc(summary='Get a Pet')
def get(self, pet_id):
# ...
@app.auth_required(auth)
@app.input(PetIn)
@app.output(PetOut)
def put(self, pet_id, data):
# ...
@app.input(PetIn(partial=True))
@app.output(PetOut)
def patch(self, pet_id, data):
# ...
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
If you want to apply a decorator for all methods, instead of repeat yourself,
you can pass the decorator to the class attribute decorators
, it accepts
a list of decorators:
class Pet(MethodView):
decorators = [auth_required(auth), doc(responses=[404])]
@app.output(PetOut)
@app.doc(summary='Get a Pet')
def get(self, pet_id):
# ...
@app.auth_required(auth)
@app.input(PetIn)
@app.output(PetOut)
def put(self, pet_id, data):
# ...
@app.input(PetIn(partial=True))
@app.output(PetOut)
def patch(self, pet_id, data):
# ...
app.add_url_rule('/pets/<int:pet_id>', view_func=Pet.as_view('pet'))
Read Flask docs on class-based views for more information.
Use abort()
to return an error response¶
Similar to Flask's abort
, but abort
from APIFlask will return a JSON response.
Example:
from apiflask import APIFlask, abort
app = APIFlask(__name__)
@app.get('/<name>')
def hello(name):
if name == 'Foo':
abort(404, 'This man is missing.')
return {'hello': name}
Tip
When app.json_errors
is True
(default), Flask's abort
will also return
JSON error response.
You can also raise an HTTPError
exception to return an error response:
from apiflask import APIFlask, HTTPError
app = APIFlask(__name__)
@app.get('/<name>')
def hello(name):
if name == 'Foo':
raise HTTPError(404, 'This man is missing.')
return {'hello': name}
The abort()
and HTTPError
accept the following arguments:
status_code
: The status code of the error (4XX and 5xx).message
: The simple description of the error. If not provided, the reason phrase of the status code will be used.detail
: The detailed information of the error, it can be used to provide additional information such as custom error code, documentation URL, etc.headers
: A dict of headers used in the error response.
Warning
The function abort_json()
was renamed to abort()
in
the version 0.4.0.
Overview of the apiflask
package¶
In the end, let's unpack the whole apiflask
package to check out what it shipped with:
APIFlask
: A class used to create an application instance (A wrapper for Flask'sFlask
class).APIBlueprint
: A class used to create a blueprint instance (A wrapper for Flask'sBlueprint
class)..@app.input()
: A decorator used to validate the input/request data from request body, query string, etc.@app.output()
: A decorator used to format the response.@app.auth_required()
: A decorator used to protect a view from unauthenticated users.@app.doc()
: A decorator used to set up the OpenAPI spec for view functions.abort()
: A function used to abort the request handling process and return an error response. The JSON version of Flask'sflask.abort()
function.HTTPError
: An exception used to return error response (used byabort()
).HTTPBasicAuth
: A class used to create an auth instance.HTTPTokenAuth
: A class used to create an auth instance.Schema
: A base class for resource schemas (Will be a wrapper for marshmallow'sSchema
).fields
: A module contains all the fields (from marshmallow).validators
: A module contains all the field validators (from marshmallow).app.get()
: A decorator used to register a route that only accepts GET request.app.post()
: A decorator used to register a route that only accepts POST request.app.put()
: A decorator used to register a route that only accepts PUT request.app.patch()
: A decorator used to register a route that only accepts PATCH request.app.delete()
: A decorator used to register a route that only accepts DELETE request.app.route()
: A decorator used to register a route. It accepts amethods
parameter to specify a list of accepted methods, default to GET only. It can also be used on theMethodView
-based view class.$ flask spec
: A command to output the spec to stdout or a file.
You can learn the details of these APIs in the API reference, or you can continue to read the following chapters.