FAQ & Troubleshooting
Common questions, issues, and solutions when working with Azu.
Frequently Asked Questions
General Questions
Q: What makes Azu different from other web frameworks?
A: Azu emphasizes compile-time type safety and contract-first development. Unlike traditional frameworks that validate at runtime, Azu catches errors during compilation, resulting in more reliable applications with zero runtime overhead for type checking.
Q: Can I use Azu for production applications?
A: Yes! Azu is built for production use with performance optimizations, comprehensive error handling, and scalability features. Many applications are successfully running Azu in production environments.
Q: How does Azu compare to other Crystal web frameworks?
A: Azu focuses on type-safe contracts and real-time features, while frameworks like Kemal prioritize simplicity. Azu provides more structure and compile-time guarantees at the cost of some flexibility.
Q: Do I need to learn Crystal to use Azu?
A: Yes, basic Crystal knowledge is required. However, Crystal's syntax is similar to Ruby, making it approachable for developers from many backgrounds.
Technical Questions
Q: How do I handle database connections in Azu?
A: Azu doesn't include a built-in ORM. You can use any Crystal database library like crystal-db
, granite
, or jennifer.cr
:
require "pg"
require "azu"
struct UserEndpoint
include Azu::Endpoint(UserRequest, UserResponse)
get "/users/:id"
def call : UserResponse
DB.open("postgres://localhost/mydb") do |db|
user = db.query_one("SELECT * FROM users WHERE id = $1",
params["id"], as: User)
UserResponse.new(user)
end
end
end
Q: Can I use Azu with existing Crystal libraries?
A: Absolutely! Azu is designed to work with the Crystal ecosystem. You can use any Crystal shard or library within your Azu applications.
Q: How do I handle authentication?
A: Implement authentication using middleware:
class AuthenticationHandler
include HTTP::Handler
def call(context)
token = context.request.headers["Authorization"]?
unless token && valid_token?(token)
context.response.status = HTTP::Status::UNAUTHORIZED
context.response.print "Authentication required"
return
end
context.set("current_user", get_user_from_token(token))
call_next(context)
end
end
# Add to middleware stack
MyApp.start [
AuthenticationHandler.new,
# ... other handlers
]
Q: How do I deploy Azu applications?
A: Compile your application and deploy the binary:
# Build for production
crystal build --release --no-debug src/my_app.cr
# Deploy binary to server
scp my_app user@server:/opt/myapp/
Common Issues
Compilation Issues
Problem: "can't infer type" errors
# ❌ This causes type inference issues
def create_user(request)
# Crystal can't infer the request type
end
# ✅ Use explicit types
def create_user(request : CreateUserRequest) : UserResponse
# Clear type information
end
Problem: Template compilation errors
Error: can't find template "users/show.html"
Solution: Check template paths in configuration:
configure do
templates.path = ["templates", "views"] # Add all template directories
template_hot_reload = env.development? # Enable hot reload for development
end
Runtime Issues
Problem: WebSocket connections not working
Symptoms:
WebSocket connection fails
No error messages in logs
Client can't connect
Solutions:
Check WebSocket route registration:
class MyChannel < Azu::Channel
ws "/websocket" # Make sure this matches client URL
def on_connect
# Implementation
end
end
Verify middleware order:
MyApp.start [
Azu::Handler::Logger.new,
# Don't put static handler before WebSocket routes
# WebSocket handlers should come early in the stack
]
Check client-side connection:
// Make sure URL matches server route
const ws = new WebSocket("ws://localhost:4000/websocket");
Problem: Request validation not working
Symptoms:
Validation rules ignored
Invalid data passes through
Solutions:
Ensure validation is called:
def call : UserResponse
# Always validate before processing
unless create_user_request.valid?
raise Azu::Response::ValidationError.new(
create_user_request.errors.group_by(&.field).transform_values(&.map(&.message))
)
end
# Process validated request
UserResponse.new(create_user(create_user_request))
end
Check validation rules syntax:
struct UserRequest
include Azu::Request
getter name : String
getter email : String
# ✅ Correct validation syntax
validate name, presence: true, length: {min: 2}
validate email, presence: true, format: /@/
def initialize(@name = "", @email = "")
end
end
Problem: File uploads not working
Symptoms:
File uploads fail silently
Uploaded files are empty
Memory issues with large files
Solutions:
Configure upload limits:
configure do
upload.max_file_size = 50.megabytes
upload.temp_dir = "/tmp/uploads"
end
Handle multipart data correctly:
struct FileUploadRequest
include Azu::Request
getter file : Azu::Params::Multipart::File?
getter description : String
def initialize(@file = nil, @description = "")
end
end
def call : FileUploadResponse
if file = file_upload_request.file
# Validate file
raise error("File too large") if file.size > 10.megabytes
# Save file
final_path = save_uploaded_file(file)
file.cleanup # Important: clean up temp file
FileUploadResponse.new(path: final_path)
else
raise error("File is required")
end
end
Performance Issues
Problem: Slow response times
Diagnosis:
Enable request logging:
MyApp.start [
Azu::Handler::Logger.new, # Add this first
# ... other handlers
]
Check for blocking operations:
def call : UserResponse
# ❌ Blocking database call
users = database.query("SELECT * FROM users") # Blocks fiber
# ✅ Use async operations when possible
users = database.async_query("SELECT * FROM users")
UserResponse.new(users)
end
Problem: Memory leaks
Common causes:
Not cleaning up file uploads
Keeping references to WebSocket connections
Large object creation in loops
Solutions:
Clean up resources:
def handle_file_upload(file)
process_file(file)
ensure
file.cleanup if file # Always cleanup
end
Manage WebSocket connections:
class MyChannel < Azu::Channel
CONNECTIONS = Set(HTTP::WebSocket).new
def on_connect
CONNECTIONS << socket.not_nil!
end
def on_close(code, message)
CONNECTIONS.delete(socket) # Remove on disconnect
end
end
Development Issues
Problem: Hot reload not working
Solutions:
Enable in configuration:
configure do
template_hot_reload = env.development? # Make sure this is true
end
Check file permissions and paths:
# Make sure template files are readable
chmod -R 644 templates/
Problem: CORS issues in development
Symptoms:
Browser blocks requests from frontend
CORS errors in console
Solution:
MyApp.start [
Azu::Handler::CORS.new(
allowed_origins: ["http://localhost:3000"], # Add your frontend URL
allowed_methods: %w(GET POST PUT PATCH DELETE OPTIONS),
allowed_headers: %w(Accept Content-Type Authorization)
),
# ... other handlers
]
Debugging Tips
Enable Debug Logging
configure do
log.level = Log::Severity::DEBUG # See all log messages
end
Inspect Request Data
def call : UserResponse
Log.debug { "Request data: #{create_user_request.inspect}" }
Log.debug { "Params: #{params.to_hash}" }
Log.debug { "Headers: #{context.request.headers}" }
# ... endpoint logic
end
Use Crystal's Built-in Debugging
# Add pp for pretty printing
require "pp"
def call : UserResponse
pp create_user_request # Pretty print request object
pp params.to_hash # Pretty print parameters
# ... endpoint logic
end
Test Individual Components
# Test request contracts in isolation
user_request = CreateUserRequest.new(name: "test", email: "test@example.com")
puts user_request.valid?
puts user_request.errors.map(&.message)
# Test response objects
user = User.new(name: "test")
response = UserResponse.new(user)
puts response.render
Performance Troubleshooting
Profile Your Application
require "benchmark"
def call : UserResponse
time = Benchmark.measure do
# Your endpoint logic here
end
Log.info { "Endpoint took #{time.total_seconds}s" }
# ... return response
end
Monitor Memory Usage
# Check memory usage during runtime
ps aux | grep my_app
# Use Crystal's built-in memory tracking
crystal run --stats src/my_app.cr
Database Query Optimization
# Log slow queries
def call : UserResponse
start_time = Time.monotonic
users = database.query("SELECT * FROM users WHERE active = true")
duration = Time.monotonic - start_time
if duration > 100.milliseconds
Log.warn { "Slow query detected: #{duration.total_milliseconds}ms" }
end
UserResponse.new(users)
end
Getting Help
Community Resources
GitHub Issues: azutoolkit/azu/issues
Crystal Community: Crystal Language Forum
Documentation: Official Azu Docs
Reporting Issues
When reporting issues, include:
Crystal version:
crystal version
Azu version: Check
shard.yml
Minimal reproduction case
Error messages and stack traces
Environment details (OS, deployment method)
Example Issue Report
**Crystal Version**: 1.10.1
**Azu Version**: 0.5.2
**OS**: macOS 13.0
**Issue**: WebSocket connection fails with "Connection refused"
**Reproduction**:
```crystal
class TestChannel < Azu::Channel
ws "/test"
def on_connect
puts "Connected"
end
end
Error: Connection refused when trying to connect to ws://localhost:4000/test
Expected: WebSocket connection should succeed
---
**Still need help?** Check the [Contributing Guide](contributing.md) for information on getting support from the community.
Last updated
Was this helpful?