Extensions¶
Callbacks let you hook into the request lifecycle to run custom logic around database operations and responses. They can be declared globally in the Flask configuration or on individual SQLAlchemy models.
Note
With AUTO_NAME_ENDPOINTS
enabled (the default), flarchitect generates a
summary for each endpoint based on its schema and HTTP method. Disable this
flag if your callbacks provide custom summaries to prevent them from being
overwritten.
Request lifecycle and hook order¶
This is the high‑level order in which flarchitect processes a request and where each callback/plugin hook sits. Understanding the flow helps you choose the right extension point and the available context.
request_started
plugin hook - Called at the very beginning of the request (@app.before_request
). - Signature:request_started(request: flask.Request) -> None
.Authentication - For routes not wrapped by
schema_constructor
, global auth runs viaArchitect._global_authentication
.For
schema_constructor
routes, auth runs inside the wrapper before schemas and rate limiting are applied.Plugin hooks around auth: -
before_authenticate(context: dict) -> dict | None
– may update context. -after_authenticate(context: dict, success: bool, user: Any | None) -> None
.context
keys:model
(type | None),method
(str), optionallyoutput_schema
/input_schema
.
Route execution (
schema_constructor
routes) a. Pluginbefore_model_op
Called with a rich context before any model operation. Return a dict to merge into context/kwargs.
Global/Model callbacks in order: -
API_GLOBAL_SETUP_CALLBACK
(method‑aware) → may mutate kwargs -API_SETUP_CALLBACK
(method‑aware) → may mutate kwargsCRUD service action runs (get/add/update/delete) - Internally may call:
API_FILTER_CALLBACK(query, model, params)
to adjust the queryAPI_ADD_CALLBACK(obj, model)
before commit on POSTAPI_UPDATE_CALLBACK(obj, model)
before commit on PATCHAPI_REMOVE_CALLBACK(obj, model)
before delete/soft‑delete on DELETE
API_RETURN_CALLBACK
(method‑aware) → adjust/replace action outputPlugin
after_model_op
(may replace the output)
Response wrapping and serialisation - Marshmallow dump occurs inside the
schema_constructor
wrapper. AfterMarshmallow serialises data,
API_DUMP_CALLBACK(data, **kwargs)
runs.The final payload is wrapped by
create_response
to a standard JSON envelope. Before the response is serialised,API_FINAL_CALLBACK
can mutate the response dictionary.Errors (raised exceptions or error statuses) trigger
API_ERROR_CALLBACK
.
request_finished
plugin hook - Runs in@app.after_request
. May return a replacementResponse
. - Signature:request_finished(request: flask.Request, response: flask.Response) -> flask.Response | None
.
Callback types¶
flarchitect recognises a number of callback hooks that allow you to run custom logic at various stages of processing:
Global setup – runs before any model-specific processing.
GLOBAL_SETUP_CALLBACK
(global: API_GLOBAL_SETUP_CALLBACK)Setup – runs before database operations. Useful for validation, logging or altering incoming data.
SETUP_CALLBACK
(global: API_SETUP_CALLBACK)Filter – lets you adjust the SQLAlchemy query object before filtering and pagination are applied.
FILTER_CALLBACK
(global: API_FILTER_CALLBACK)Add – called before a new object is committed to the database.
ADD_CALLBACK
(global: API_ADD_CALLBACK)Update – invoked prior to persisting updates to an existing object.
UPDATE_CALLBACK
(global: API_UPDATE_CALLBACK)Remove – executed before an object is deleted.
REMOVE_CALLBACK
(global: API_REMOVE_CALLBACK)Return – runs after the database operation but before the response is returned. Ideal for adjusting the output or adding headers.
RETURN_CALLBACK
(global: API_RETURN_CALLBACK)Dump – executes after Marshmallow serialisation allowing you to modify the dumped data.
DUMP_CALLBACK
(global: API_DUMP_CALLBACK)Final – runs immediately before the response is sent to the client.
FINAL_CALLBACK
(global: API_FINAL_CALLBACK)Error – triggered when an exception bubbles up; handle logging or notifications here.
ERROR_CALLBACK
(global: API_ERROR_CALLBACK)
Configuration¶
Callbacks are referenced by the following configuration keys (global variants
use API_<KEY>
):
GLOBAL_SETUP_CALLBACK
/ API_GLOBAL_SETUP_CALLBACKSETUP_CALLBACK
/ API_SETUP_CALLBACKFILTER_CALLBACK
/ API_FILTER_CALLBACKADD_CALLBACK
/ API_ADD_CALLBACKUPDATE_CALLBACK
/ API_UPDATE_CALLBACKREMOVE_CALLBACK
/ API_REMOVE_CALLBACKRETURN_CALLBACK
/ API_RETURN_CALLBACKDUMP_CALLBACK
/ API_DUMP_CALLBACKFINAL_CALLBACK
/ API_FINAL_CALLBACKERROR_CALLBACK
/ API_ERROR_CALLBACK
You can apply these keys in several places:
Global Flask config
Use
API_<KEY>
to apply a callback to all endpoints.class Config: API_SETUP_CALLBACK = my_setup
Model config
Set lowercase attributes on a model’s
Meta
class to apply callbacks to all endpoints for that model.class Author(db.Model): class Meta: setup_callback = my_setup
Model method config
Use
<method>_<key>
on theMeta
class for the highest level of specificity.class Author(db.Model): class Meta: get_return_callback = my_get_return
Callback signatures¶
Setup, Global setup and filter¶
Setup‑style callbacks receive model
and a set of keyword arguments
describing the operation. They must return a dict (possibly empty) which will
be merged into the route’s processing context.
Common **kwargs
keys (availability depends on the route):
- id
: int | str | None – primary key value for single‑item routes
- field
: str | None – alternative lookup field name when configured
- join_model
: type | None – relationship model for relation routes
- output_schema
: marshmallow.Schema | None – response schema
- relation_name
: str | None – relation attribute name (relation routes)
- deserialized_data
: dict | None – request body deserialised by the input schema
- many
: bool – whether the route returns a collection
- method
: str – HTTP method (e.g., “GET”)
Return value: dict[str, Any]
to merge back into kwargs.
Examples: .. code-block:: python
- def my_setup_callback(model, **kwargs):
# modify kwargs as needed return kwargs
- def my_filter_callback(query, model, params):
return query.filter(model.id > 0)
Add, update and remove¶
These callbacks receive the SQLAlchemy object instance and must return it:
def my_add_callback(obj, model):
obj.created_by = "system"
return obj
Return¶
Return callbacks receive model
, output
and **kwargs
(same keys as
Setup). They must return a dict containing the output
key. The callback can
wrap or transform the output. Typical shapes for output
are:
- GET many: {"query": list[Model], "limit": int, "page": int, "total_count": int}
- GET one: {"query": Model}
- POST/PATCH: the created/updated model instance or a result dict
- DELETE: (None, 200)
when soft‑delete/OK
def my_return_callback(model, output, **kwargs):
return {"output": output}
Dump¶
Dump callbacks accept data
and **kwargs
and must return the data:
def my_dump_callback(data, **kwargs):
data["name"] = data["name"].upper()
return data
Final¶
Final callbacks receive the response dictionary before it is serialised:
def my_final_callback(data):
data["processed"] = True
return data
Error¶
Error callbacks receive the error message, status code and a value payload constructed by the response wrapper. Use this to send notifications or add structured logs.
def my_error_callback(error, status_code, value):
log_exception(error)
Plugin hooks¶
Plugins provide a structured way to observe and influence behaviour across the
app. Configure with API_PLUGINS
as a list of classes/instances/factories
deriving from flarchitect.plugins.PluginBase
.
Available hooks and signatures:
- request_started(request: flask.Request) -> None
- request_finished(request: flask.Request, response: flask.Response) -> flask.Response | None
- before_authenticate(context: dict[str, Any]) -> dict[str, Any] | None
Context keys:
model
(type | None),method
(str), optionaloutput_schema
/input_schema
.
after_authenticate(context: dict[str, Any], success: bool, user: Any | None) -> None
before_model_op(context: dict[str, Any]) -> dict[str, Any] | None
- Context keys mirror Setup kwargs, plusmethod
(str) andmany
(bool).after_model_op(context: dict[str, Any], output: Any) -> Any | None
spec_build_started(spec: Any) -> None
spec_build_completed(spec_dict: dict[str, Any]) -> dict[str, Any] | None
Example plugin:
from flarchitect.plugins import PluginBase
class AuditPlugin(PluginBase):
def before_model_op(self, context):
# attach correlation fields
return {"audit": {"path": context.get("relation_name"), "method": context["method"]}}
def after_model_op(self, context, output):
# inject audit trail into result dicts
if isinstance(output, dict):
out = dict(output)
out["audit"] = context.get("audit")
return out
return None
Extending query parameters¶
Use ADDITIONAL_QUERY_PARAMS to document extra query parameters introduced in a return callback. The value is a list of OpenAPI parameter objects.
class Config:
API_ADDITIONAL_QUERY_PARAMS = [{
"name": "log",
"in": "query",
"description": "Log call into the database",
"schema": {"type": "string"},
}]
class Author(db.Model):
class Meta:
get_additional_query_params = [{
"name": "log",
"in": "query",
"schema": {"type": "string"},
}]
Acceptable types¶
schema.type
may be one of:
string
number
integer
boolean
array
object
Acceptable formats¶
Common schema.format
values include:
date
date-time
password
byte
binary
email
phone
postal_code
uuid
uri
hostname
ipv4
ipv6
int32
int64
float
double
For comprehensive configuration details see Configuration.