Plugins ======= flarchitect supports a lightweight plugin system for observing and customising behaviour at well-defined hook points. Use plugins for cross-cutting concerns such as auditing, metrics, tenant scoping, or output shaping that shouldn’t live in route handlers. Quick start ----------- 1. Implement a plugin by subclassing ``flarchitect.plugins.PluginBase``:: from flarchitect.plugins import PluginBase class AuditPlugin(PluginBase): def before_model_op(self, context): # e.g. attach actor/tenant to the payload data = context.get("deserialized_data") or {} if isinstance(data, dict): data = {**data, "actor": context.get("request_id")} return {"deserialized_data": data} def after_model_op(self, context, output): # e.g. emit an audit log print("AUDIT:", context.get("method"), context.get("model"), output) 2. Register your plugin via Flask config:: app.config["API_PLUGINS"] = [AuditPlugin()] Hook reference -------------- Stable hook signatures (kwargs may grow over time): - request_started(request: flask.Request) -> None Called at the beginning of each request. - request_finished(request: flask.Request, response: flask.Response) -> flask.Response | None Called after a response is created. Return a replacement Response to override. - before_authenticate(context: dict) -> dict | None Runs prior to authentication (for non-schema routes and schema routes alike). May return a dict of updates to merge into the context. - after_authenticate(context: dict, success: bool, user: Any | None) -> None Runs after authentication attempt. - before_model_op(context: dict) -> dict | None Runs before a CRUD action. Context includes keys such as ``model``, ``method``, ``many``, ``id``, ``field``, ``join_model``, ``output_schema`` and (for POST/PATCH) ``deserialized_data``. Return a dict to update the call-time kwargs (e.g., mutate ``deserialized_data``). - after_model_op(context: dict, output: Any) -> Any | None Runs after a CRUD action. Return a value to replace the output before serialisation. - spec_build_started(spec: apispec.APISpec) -> None Called when building the OpenAPI specification. - spec_build_completed(spec_dict: dict) -> dict | None Called after the spec is converted to a dictionary. Return a dict to replace it. Notes ----- - Plugins are additive: multiple plugins can be installed; they are called in order. - Returning ``None`` means "no change". Where supported, the first non-``None`` return value wins (e.g., response replacement). - Existing callback config keys (e.g., ``API_SETUP_CALLBACK``) continue to work and compose with plugins.