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:
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:
If you want to make the application restart whenever the code changes, you can enable
reloader with --reload option:
$flaskrun--reload
Tip
Install watchdog for a better performance for the application reloader:
$pip3installwatchdog
> 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 run --debug
If you are not using the latest Flask version (>=2.2.3), you will need to set
the environment variable FLASK_DEBUG to True instead:
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:
$pip3installpython-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 .flaskenvFLASK_APP=helloFLASK_DEBUG=1
While the secrets values should save in the .env file:
# save as .envSECRET_KEY=some-random-stringDATABASE_URL=your-database-urlFOO_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):
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_DEBUG
in .flaskenv file to find the app instance from given import path and enable the
debug mode:
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.
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:
By the way, you can mix the use of app.route() with the shortcuts in your
application.
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
ncoming data. APIFlask supports two main approaches: marshmallow schemas
and Pydantic models.
See Data Schema chapter for the details of how to write a schema and
the examples for all the fields and validators.
A marshmallow schema class should inherit the apiflask.Schema class.
Fields are represented with field classes in apiflask.fields.
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.
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:
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 is used to create a new pet.
For marshmallow schema:
fromapiflaskimportAPIFlaskapp=APIFlask(__name__)# ... PetIn schema definition here ...@app.post('/pets')@app.input(PetIn)defcreate_pet(json_data):# the json_data is a dictprint(json_data)return{'message':'created'},201
For Pydantic model:
fromapiflaskimportAPIFlaskapp=APIFlask(__name__)# ... PetIn schema definition here ...@app.post('/pets')@app.input(PetIn)defcreate_pet(json_data:PetIn):# the json_data is a PetIn instanceprint(json_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 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)
If the validation passed, the data will inject into the view function as
a keyword argument named {location}_data (e.g. json_data). Otherwise, an error response with the detail of the validation
result will be returned.
The form of the injected data depends on the schema type:
For marshmallow schema, the data will be a dict.
For Pydantic model, the data will be an instance of the model.
If you want to disable validation on input, you can set validation=False. Now the
input data will be passed to the view function without validation. If no input data,
the view function will receive an empty dict:
Both approaches will automatically serialize your data and generate OpenAPI documentation.
Output Validation Behavior
Unlike marshmallow, Pydantic validates output data before sending responses. This means that if your view function returns data that does not conform to the output model schema, a 500 Internal Server Error will be raised.
Tip
For marshmallow schemas: You can set a default value for output field with the dump_default argument:
name=String(dump_default='default name')
For Pydantic models: You can set default values directly in the field definition:
name:str=Field("default name",description="Pet name")is_active:bool=True# Simple default value
Now add it to the view function which used to get a pet resource:
fromapiflaskimportAPIFlaskapp=APIFlask(__name__)# ... PetOut schema definition here ...@app.get('/pets/<int:pet_id>')@app.output(PetOut)defget_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:
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:
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:
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 of OpenAPI
objct are accessible with configuration variables.
The tag object, Operation's summary and description will generated from
the blueprint name, the view function name and docstring.
You can register a spec processor function to process the spec.
requestBody and responses fields can be set with the input and output
decorator.
Other operation fields can be set with the @app.doc decorator:
fromapiflaskimportAPIFlaskapp=APIFlask(__name__)@app.get('/hello')@app.doc(summary='Say hello',description='Some description for the /hello')defhello():return'Hello'
See Use the doc decorator for more details
about OpenAPI generating 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.).
To implement an HTTP Basic authentication, you will need to:
Create an auth object with HTTPBasicAuth
Register a callback function with @auth.verify_password, the function
should accept username and password, return the corresponding user object
or None.
Protect the view function with @app.auth_required(auth).
Access the current user object in your view function with auth.current_user.
fromapiflaskimportAPIFlask,HTTPBasicAuthapp=APIFlask(__name__)auth=HTTPBasicAuth()# create the auth object@auth.verify_passworddefverify_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)defhello():returnf'Hello, {auth.current_user}!'
Tip
To access the current user object, you need to use auth.current_user property in the view function.
It's equivalent to using auth.current_user() method in Flask-HTTPAuth.
To implement an HTTP Bearer authentication, you will need to:
Create an auth object with HTTPTokenAuth
Register a callback function with @auth.verify_token, the function
should accept token, return the corresponding user object or None.
Protect the view function with @app.auth_required(auth).
Access the current user object in your view function with auth.current_user.
fromapiflaskimportAPIFlask,HTTPTokenAuthapp=APIFlask(__name__)auth=HTTPTokenAuth()# create the auth object# or HTTPTokenAuth(scheme='Bearer')@auth.verify_token# register a callback to verify the tokendefverify_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 viewdefhello():# access the current user with auth.current_userreturnf'Hello, {auth.current_user}!'
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.
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:
classPet(MethodView):defget(self,pet_id):# triggered by GET requestreturn{'message':'OK'}defpost(self,pet_id):# triggered by POST requestreturn{'message':'OK'}defput(self,pet_id):# triggered by PUT requestreturn{'message':'OK'}defdelete(self,pet_id):# triggered by DELETE requestreturn'',204defpatch(self,pet_id):# triggered by PATCH requestreturn{'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 classes. For example, register the post method
to a different URL rule than the others:
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:
classPet(MethodView):@app.output(PetOut)@app.doc(summary='Get a Pet')defget(self,pet_id):# ...@app.auth_required(auth)@app.input(PetIn)@app.output(PetOut)defput(self,pet_id,json_data):# ...@app.input(PetIn(partial=True))@app.output(PetOut)defpatch(self,pet_id,json_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:
classPet(MethodView):decorators=[auth_required(auth),doc(responses=[404])]@app.output(PetOut)@app.doc(summary='Get a Pet')defget(self,pet_id):# ...@app.auth_required(auth)@app.input(PetIn)@app.output(PetOut)defput(self,pet_id,json_data):# ...@app.input(PetIn(partial=True))@app.output(PetOut)defpatch(self,pet_id,json_data):# ...app.add_url_rule('/pets/<int:pet_id>',view_func=Pet.as_view('pet'))
Similar to Flask's abort, but abort from APIFlask will return a JSON response.
Example:
fromapiflaskimportAPIFlask,abortapp=APIFlask(__name__)@app.get('/<name>')defhello(name):ifname=='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:
fromapiflaskimportAPIFlask,HTTPErrorapp=APIFlask(__name__)@app.get('/users/<name>')defhello(name):ifname=='Foo':raiseHTTPError(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.
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's Flask class).
APIBlueprint: A class used to create a blueprint instance (A wrapper for Flask's Blueprint 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's flask.abort() function.
HTTPError: An exception used to return error response (used by abort()).
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's Schema).
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 a methods
parameter to specify a list of accepted methods, default to GET only. It can also
be used on the MethodView-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.