Extending with Hooks

Chapter 9: Hooking into sparQ's lifecycle to customize behavior.

Hooks let your app run code at specific points in sparQ's lifecycle—when the app starts, when the database initializes, or when certain events occur. They're essential for plugins and useful for apps that need to set up data or configuration.

The module.py File

Hooks are defined in your app's module.py file:

myapp/ ├── __init__.py ├── __manifest__.py ├── module.py # Define hooks here └── ...

When sparQ loads your module, it looks for specific function names in this file and calls them at the right time.

Available Hooks

init_app(app)

Called when the Flask application initializes. The app parameter is the Flask application instance.

# module.py
def init_app(app):
    """Called when Flask app initializes."""
    # Register custom configuration
    app.config['MYAPP_FEATURE_ENABLED'] = True

    # Register a custom error handler
    @app.errorhandler(CustomError)
    def handle_custom_error(e):
        return render_template('myapp/desktop/error.html', error=e), 500

    # Register a template filter
    @app.template_filter('reverse')
    def reverse_filter(s):
        return s[::-1]

Use this hook for:

init_database(db)

Called after database tables are created. The db parameter is the SQLAlchemy database instance.

# module.py
def init_database(db):
    """Called after db.create_all()."""
    from .models.category import Category

    # Seed default data if table is empty
    if Category.query.count() == 0:
        defaults = ['Work', 'Personal', 'Shopping', 'Health']
        for name in defaults:
            Category.create(name=name)
        print("Created default categories")

Use this hook for:

Check before inserting. Always check if data exists before inserting. This hook runs every time sparQ starts, not just on first install.

Practical Examples

Setting Up Default Settings

# module.py
def init_database(db):
    """Create default settings for new installations."""
    from .models.setting import Setting

    defaults = {
        'notifications_enabled': 'true',
        'items_per_page': '20',
        'theme': 'light'
    }

    for key, value in defaults.items():
        if not Setting.get(key):
            Setting.create(key=key, value=value)

Registering Custom Jinja Globals

# module.py
def init_app(app):
    """Add custom functions available in all templates."""
    from .models.category import Category

    @app.context_processor
    def inject_categories():
        # Now {{ categories }} is available in all templates
        return {'categories': Category.get_all()}

Adding Request Hooks

# module.py
def init_app(app):
    """Run code before/after each request."""

    @app.before_request
    def log_request():
        # Log every request (be careful with performance)
        app.logger.debug(f"Request: {request.path}")

    @app.after_request
    def add_header(response):
        # Add custom headers to every response
        response.headers['X-MyApp-Version'] = '1.0.0'
        return response

Integrating Third-Party Services

# module.py
def init_app(app):
    """Set up external service connections."""
    import redis

    # Create Redis connection
    app.redis = redis.from_url(app.config.get('REDIS_URL', 'redis://localhost'))

    # Now accessible as current_app.redis in controllers

Hook Execution Order

When sparQ starts, hooks execute in this order:

  1. Core module init_app()
  2. Dashboard module init_app()
  3. Team module init_app()
  4. All other base modules init_app() (alphabetically)
  5. All apps init_app() (alphabetically)
  6. Database tables created
  7. Core module init_database()
  8. Dashboard module init_database()
  9. Team module init_database()
  10. All other base modules init_database() (alphabetically)
  11. All apps init_database() (alphabetically)

This order matters if your app depends on another module's setup.

register_ai_tools(registry)

Called during tool collection for the AI agent. The registry parameter is a ToolRegistry instance where you register your tools.

# module.py
from system.module.hooks import hookimpl

class MyAppModule:
    @hookimpl
    def register_ai_tools(self, registry):
        """Register AI tools for this module."""
        from .tools import create_widget, search_widgets

        registry.register(create_widget)
        registry.register(search_widgets)

Use this hook for:

See the AI Tools guide for complete documentation on creating tools.

Plugin Hooks

Plugins can hook into additional events using the pluggy system:

# module.py
import pluggy

hookimpl = pluggy.HookimplMarker("sparq")

@hookimpl
def on_user_created(user):
    """Called when a new user is created."""
    # Send welcome email, create default data, etc.
    send_welcome_email(user.email)

@hookimpl
def on_invoice_paid(invoice):
    """Called when an invoice is marked as paid."""
    # Update inventory, send receipt, etc.
    update_inventory(invoice.items)

Available plugin hooks depend on which base modules are installed. Check each module's documentation for available hooks.

Key Takeaways