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:
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:
- Setting configuration values
- Registering error handlers
- Adding template filters or globals
- Setting up third-party extensions
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:
- Seeding default data
- Creating required records
- Running data migrations
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:
- Core module
init_app() - Dashboard module
init_app() - Team module
init_app() - All other base modules
init_app()(alphabetically) - All apps
init_app()(alphabetically) - Database tables created
- Core module
init_database() - Dashboard module
init_database() - Team module
init_database() - All other base modules
init_database()(alphabetically) - 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:
- Registering tools that sparQy can use
- Making your module's features accessible via natural language
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
- Define hooks in
module.py init_app()runs when Flask initializes—use for configurationinit_database()runs after tables are created—use for seeding data- Always check if data exists before inserting
- Hooks run in module load order