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.

  1. request_started plugin hook - Called at the very beginning of the request (@app.before_request). - Signature: request_started(request: flask.Request) -> None.

  2. Authentication - For routes not wrapped by schema_constructor, global auth runs via

    Architect._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), optionally output_schema / input_schema.

  3. Route execution (schema_constructor routes) a. Plugin before_model_op

    • Called with a rich context before any model operation. Return a dict to merge into context/kwargs.

    1. Global/Model callbacks in order: - API_GLOBAL_SETUP_CALLBACK (method‑aware) → may mutate kwargs - API_SETUP_CALLBACK (method‑aware) → may mutate kwargs

    2. CRUD service action runs (get/add/update/delete) - Internally may call:

      • API_FILTER_CALLBACK(query, model, params) to adjust the query

      • API_ADD_CALLBACK(obj, model) before commit on POST

      • API_UPDATE_CALLBACK(obj, model) before commit on PATCH

      • API_REMOVE_CALLBACK(obj, model) before delete/soft‑delete on DELETE

    3. API_RETURN_CALLBACK (method‑aware) → adjust/replace action output

    4. Plugin after_model_op (may replace the output)

  4. Response wrapping and serialisation - Marshmallow dump occurs inside the schema_constructor wrapper. After

    Marshmallow 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.

  5. request_finished plugin hook - Runs in @app.after_request. May return a replacement Response. - 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>):

You can apply these keys in several places:

  1. Global Flask config

    Use API_<KEY> to apply a callback to all endpoints.

    class Config:
        API_SETUP_CALLBACK = my_setup
    
  2. 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
    
  3. Model method config

    Use <method>_<key> on the Meta 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), optional output_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, plus method (str) and many (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.