Feliz (with Flask)
A framework designed to assist in using Flask.
Template
If you want to use the template of Feliz, you can clone the following repository:
feliz-template
Features
- Rapid API Development: Offers decorators and utility functions to quickly establish RESTful APIs with minimal code.
- Configuration Management: Built-in support for managing configurations across different development stages (development, testing, production).
- Error Handling: Simplifies the process of handling errors and customizing responses for a better API user experience.
- Database Integration: Facilitates easier integration with databases, providing abstraction layers for common operations. Supporting PostgresDB and MangoDB.
- Authentication & Authorization: Includes ready-to-use modules for handling user authentication and authorization.
Getting Started
Environment
Python 3.9
or later
Installation
Install Feliz easily with pip, which will also install all required dependencies:
pip install feliz
Dependencies
feliz
flask
flask-cors
flask_jwt_extended
bcrypt
pyyaml
Configurations
server_config.yaml
$ cd {your_project}/configs/private/server_config.yaml
This is the configuration file of the server. You can set the server name, host, port, debug mode, number of workers, etc.
If needed, you can add more configurations to this file.
SERVER:
NAME:
HOST:
PORT:
DEBUG:
WORKERS:
JWT:
JWT_ENABLE:
JWT_ALGORITHM:
JWT_SECRET_KEY:
EXPIRE_HOURS:
MESSAGE:
UNAUTHORIZED:
INVALID:
EXPIRED:
REVOKED:
CORS:
CORS_ENABLE:
DB:
DB_ENABLE:
INI_FILE:
API:
API_ENABLE:
API_FILE:
database.ini
$ cd {your_project}/configs/private/database.ini
(recommended path)
This is the configuration file of the database. You can set the database type, host, port, database name, username, password, etc.
If needed, you can add more sections to this file.
[<your_section_name>]
db_type=
host=
port=
database=
username=
password=
[<another_section_name>]
...
server_api.yaml
$ cd {your_project}/configs/public/server_api.yaml
This is the configuration file of the API. If you use @handler
decorator, you need to set the configuration of the API and not forget to turn the API_ENABLE
to True
in server_config.yaml
.
admin:
accounts:
POST:
Description: "add_accounts"
Authentication: True
Permission: ["1", "2", "3", "4", "e"]
Mandatory: ["add_list"]
Optionals: ["return_added"]
OptionalDefaults: [False]
InputInspect: True
InputType: { "add_list": list, "return_added": bool }
ç
- The url of each API follows the format:
http://{host}:{port}/api/{service}/{opration}
- Each element in
Permission
is used to classify the user's group and no cascading relationship between them. Optionals
and OptionalDefaults
must be the same length.- About
InputType
, no need to set all the parameters, only set the parameters that need to be inspected. InputType
supports all types which Python supports. Besides, Feliz supports json
type, which inspect whether the input parameter can be json loads. Moreover, Feliz also supports json-list
and json-dict
types, which inspect whether the input parameter can be json loads and the type of the loaded data is list or dict.
Usage
Initialware & InitialwareSystem
Initialware is a class that handles a part of the initialization of the application. For example, you can use it load the configuration file, set up the jwt, create the tables in the database, etc.
InitialwareSystem is a class that manage the initialware. You can use it to add initialware to this class, and decide the order of the initialware.
Feliz provides some initialware for you to use, such as ImportGlobals
, JWTInitialware
, CorsInitialware
, _DatabaseInitialware
including PostgresInitialware
and MongoInitialware
.
The following is an example of how to use Initialware and InitialwareSystem:
from flask import Flask
from feliz.initialware_tools import InitialwareSystem, ImportGlobals, JwtInitialware, CorsInitialware
from feliz_db.postgres_tools import PostgresHandler
from models import models
app = Flask(__name__)
iws = InitialwareSystem()
iws.use(ImportGlobals())
iws.use(JwtInitialware())
iws.use(CorsInitialware())
iws.use(PostgresInitialware(PostgresHandler, models["postgres"]))
iws.execute(app)
Note:
models
is a dictionary that contains the models of the database. If you want to learn more about the models, please refer to the feliz_db
Middleware & MiddlewareSystem
Middleware is a class that handles the part outside (i.e. before and after entering) a API handler. For example, you can use it to check the user's permission, inspect the input parameters, and close the database connection after the request is completed.
MiddlewareSystem is a class that manage the middleware. You can use it to add middleware to this class, and decide the order of the middleware.
Feliz provides some middleware for you to use, such as the fllowing:
- Private Middleware:
_PassGlobal
, _InputRequest
, _JWTMiddleware
- Auth Middleware:
_AuthMiddleware
, UserExistence
, UserDatabasePermission
, UserApiPermission
, UserStatusCheck
- SafeKeys Middleware:
_SafeKeysMiddleware
, SafeMandatoryKeys
, SafeInputType
- Response Middleware:
JsonifyResponse
MiddlewareSystem is used in before_request and after_request of Flask. The following is an example of how to use Middleware and MiddlewareSystem:
from flask import request
from feliz.middleware_tools import MiddlewareSystem, UserExistence, UserDatabasePermission, UserApiPermission, SafeMandatoryKeys, SafeInputType, JsonifyResponse, UserStatusCheck
@app.before_request
def before_request():
"""
This function is used to handle the request before the request is handled.
"""
DH = get_db("postgres", "mlt")
mws = MiddlewareSystem()
mws.use(SafeMandatoryKeys())
mws.use(SafeInputType())
mws.use(UserExistence(DH, "admin.user_list", "user_id"))
mws.use(UserStatusCheck(DH, "admin.user_list", "user_id"))
mws.use(UserDatabasePermission(DH, "admin.user_list", "user_id"))
mws.use(UserApiPermission())
mws.process_request(request)
@app.after_request
def after_request(response):
"""
This function is used to handle the response after the request is handled.
"""
mws = MiddlewareSystem()
mws.use(JsonifyResponse())
mws.process_response(response)
return response
Handler Decorator
Feliz provides a decorator called handler
to help you handle the API. It contains route decorator of Flask, and it can pass the parameters of the API to the handler function.
The parameters of the handler decorator are as follows:
- input_request: the input parameters of the API.
- DB: the database handler.
- CONFIGS: the configuration of the server. (server_config.yaml)
- API_CONFIGS: the configuration of the API. (server_api.yaml)
- USER_DATA: the information of the user.
All the parameters are encapsulated in a parameter called params
, and you can decide which parameters to use in the handler function. (Note that params
is the required parameter of the handler function.)
Response
To communicate the outcome of operations to the client, we provide two response functions:
TrueResponse(message= {message}, content= {content})
for successful operations, returning {"indicator": True, "message": message, "content": content}
.
FalseResponse(message= {message})
for unsuccessful operations, returning {"indicator": False, "message": message}
.
These ensure a standardized way of conveying success or failure, along with any relevant message and content.
Example
The following is an example of how to use handler decorator:
In apis/admin_api.py
:
from flask import Blueprint
from feliz.api_tools import handler, TrueResponse, FalseResponse
from feliz_db.postgres_tools import PostgresHandler
adminApi = Blueprint("admin", __name__)
@handler("/accounts", adminApi, methods=["POST"])
def add_accounts(input_request, DB, **params):
"""
This function is used to add accounts.
"""
add_list, return_added = input_request["add_list"], input_request["return_added"]
DH: PostgresHandler = DB["postgres"]["mlt"]
add_res = DH.add_data("admin.accounts", add_list)
if add_res["indicator"]:
if return_added:
get_res = DH.get_data("admin.accounts", conditional_rule_list= [("user_id=", add_list[0]["user_id"])])
if get_res["indicator"]:
return TrueResponse(get_res["message"], get_res["formatted_data"])
else:
return FalseResponse(get_res["message"])
else:
return TrueResponse(add_res["message"])
else:
return FalseResponse(add_res["message"])
Register in app.py
:
from feliz.api_tools import api_route_register
from apis.admin_api import adminApi
app = Flask(__name__)
api_route_register(app, adminApi)
Author