This directory demonstrates how to integrate CQL's advanced features with popular Crystal web frameworks. Learn how to seamlessly add CQL's caching, performance monitoring, and ORM capabilities to your web applications.
Purpose: Comprehensive integration example with the Azu framework Repository: https://github.com/azutoolkit/azu Features:
- HTTP Handler integration for automatic request-scoped caching
- Controller integration with before/after hooks
- Manual hook integration for custom setups
- Performance benchmarking and optimization
- Real-world application patterns
- Multiple integration approaches to fit different application structures
Run: crystal azu_query_cache_demo.cr
CQL provides first-class support for the Azu toolkit with multiple integration options:
require "cql/cache/middleware"
server = HTTP::Server.new([
CQL::Cache::Middleware::Azu::Handler.new,
YourAzuApp.new
])app = YourAzuApp.new
CQL::Cache::Middleware::Azu.setup!(app)class ApplicationController < Azu::Controller
include CQL::Cache::Middleware::Azu::Controller
endCQL also supports Kemal with middleware patterns:
require "cql/cache/middleware"
# Add to your Kemal app
before_all do |env|
CQL::Cache::Middleware::Kemal.before_request(env)
end
after_all do |env|
CQL::Cache::Middleware::Kemal.after_request(env)
end- Query Deduplication - Eliminate duplicate queries within requests
- Zero Configuration - Works out of the box with minimal setup
- Thread Safety - Safe for concurrent request handling
- Request Isolation - Cache automatically clears between requests
- 2-10x Faster - Typical speedup for applications with query duplication
- Memory Efficient - Request-scoped caching prevents memory leaks
- Detailed Statistics - Monitor cache effectiveness and performance
- Multiple Integration Options - Choose the pattern that fits your app
- Minimal Code Changes - Add caching without restructuring code
- Framework Agnostic - Consistent API across different frameworks
- Production Ready - Battle-tested in real applications
- Crystal 1.16.3+
- SQLite3 (for examples)
- Basic familiarity with your chosen web framework
# 1. Add CQL to your dependencies
dependencies:
cql:
github: crystal-lang/cql
# 2. Require and configure
require "cql"
require "cql/cache/middleware"
CQL.configure do |c|
c.db = ENV["DATABASE_URL"]
c.cache.on = true
c.cache.request_cache = true
end
# 3. Add to your Azu app
class BlogApp < Azu::Application
use CQL::Cache::Middleware::Azu::Handler.new
# Your routes and handlers
endclass BlogApp < Azu::Application
# Enable per-request query caching
use CQL::Cache::Middleware::Azu::Handler.new
get "/articles" do |ctx|
# First query - hits database
articles = Article.published.preload(:author)
# If this same query runs again in the same request
# (e.g., in a partial or helper), it hits the cache
popular_articles = Article.published.preload(:author)
render_template "articles/index.html", {
articles: articles,
popular: popular_articles
}
end
get "/dashboard" do |ctx|
# Multiple related queries that might be repeated
user_count = User.count # Database hit
article_count = Article.count # Database hit
published_count = Article.published.count # Database hit
# If any of these queries run again, they hit the cache
stats = {
users: User.count, # Cache hit
articles: Article.count, # Cache hit
published: Article.published.count # Cache hit
}
render_json stats
end
endclass APIService < Azu::Application
include CQL::Cache::Middleware::Azu::Controller
before_action :start_query_cache
after_action :end_query_cache
get "/api/users/:id" do |ctx|
user_id = ctx.params["id"].to_i
# These queries will be automatically cached per request
user = User.find(user_id)
posts = user.posts.published
comments = user.comments.recent
# Second call to same queries hits cache
user_data = {
user: User.find(user_id), # Cache hit
posts: user.posts.published, # Cache hit
comments: user.comments.recent # Cache hit
}
render_json user_data
end
end# Development
CQL.configure do |c|
c.db = "sqlite3://./db/development.db"
c.cache.on = true
c.cache.request_cache = true
c.cache.request_size = 100 # Smaller cache for development
end
# Production
CQL.configure do |c|
c.db = ENV["DATABASE_URL"]
c.cache.on = true
c.cache.request_cache = true
c.cache.request_size = 1000 # Larger cache for production
c.cache.redis_url = ENV["REDIS_URL"] # Persistent cache
end# Custom request cache middleware
class CustomCacheMiddleware
def call(context)
request_id = context.request.headers["X-Request-ID"]? || UUID.random.to_s
CQL::Cache::RequestQueryCacheHelper.start_request(request_id)
begin
response = call_next(context)
# Log cache statistics for monitoring
stats = CQL::Cache::RequestQueryCacheHelper.stats
Log.info { "Request cache stats: #{stats}" }
response
ensure
CQL::Cache::RequestQueryCacheHelper.end_request
end
end
endclass PerformanceMiddleware
def call(context)
# Start performance monitoring for this request
CQL::Performance.monitor.set_context(
endpoint: context.request.path,
method: context.request.method,
user_id: current_user_id(context)
)
start_time = Time.monotonic
response = call_next(context)
duration = Time.monotonic - start_time
# Log performance metrics
metrics = CQL::Performance.monitor.metrics_summary
Log.info {
"Request performance: #{duration.total_milliseconds}ms, " \
"queries: #{metrics.total_queries}, " \
"slow: #{metrics.slow_queries}"
}
response
end
end# Add to your development environment
if ENV["CRYSTAL_ENV"] == "development"
after_all do |env|
# Generate beautiful performance report in development
CQL::Performance.monitor.generate_comprehensive_report("logger")
end
enddescribe "Request Cache Integration" do
it "should deduplicate queries within a request" do
# Simulate request start
CQL::Cache::Middleware::Azu.before_request(mock_context)
# Execute duplicate queries
result1 = User.where(active: true).all
result2 = User.where(active: true).all
# Verify cache effectiveness
stats = CQL::Cache::RequestQueryCache.instance.stats
expect(stats["hits"]).to be > 0
# Cleanup
CQL::Cache::Middleware::Azu.after_request(mock_context)
end
enddescribe "Framework Performance" do
it "should show significant speedup with caching" do
# Measure without cache
CQL::Cache::RequestQueryCacheHelper.enabled = false
without_cache_time = benchmark_request("/api/dashboard")
# Measure with cache
CQL::Cache::RequestQueryCacheHelper.enabled = true
with_cache_time = benchmark_request("/api/dashboard")
# Verify improvement
speedup = without_cache_time / with_cache_time
expect(speedup).to be > 2.0 # At least 2x speedup
end
end- Repository: https://github.com/azutoolkit/azu
- Documentation: https://azutopia.gitbook.io/azu/
- CQL Integration: Built-in support with multiple patterns
- Repository: https://github.com/kemalcr/kemal
- CQL Integration: Middleware-based integration available
- Integration examples coming soon
- Custom adapter patterns available
- ../caching/ - Learn the caching features being integrated
- ../performance/ - Monitor your web application performance
- ../blog/ - See a complete web application example
Ready to supercharge your web framework with CQL? Start with the Azu integration and adapt the patterns to your chosen framework! 🌐