### Complete Kemal Configuration Example
Source: https://kemalcr.com/guide
A comprehensive example demonstrating how to configure various aspects of a Kemal application, including server settings, static files, logging, SSL, headers, error handling, custom handlers, and routes.
```crystal
require "kemal"
# Server settings
Kemal.config.host_binding = "0.0.0.0"
Kemal.config.port = 3000
Kemal.config.env = "production"
Kemal.config.max_request_body_size = 1024 * 1024 * 10 # 10 MB limit
# Static files
Kemal.config.public_folder = "./public"
Kemal.config.serve_static = {"gzip" => true, "dir_listing" => false}
# Logging
Kemal.config.logging = true
# SSL
Kemal.config.ssl = true
Kemal.config.ssl_certificate_file = "./ssl/cert.pem"
Kemal.config.ssl_key_file = "./ssl/key.pem"
# Headers
Kemal.config.powered_by_header = "MyApp/1.0"
# Error handling
Kemal.config.always_rescue = true
# Add custom handler
Kemal.config.add_handler MyAuthHandler.new
# Your routes go here
get "/" do
"Hello World!"
end
Kemal.run
```
--------------------------------
### Create and Run a Basic Kemal Application
Source: https://kemalcr.com/guide
Demonstrates how to define a simple route and start the Kemal server using Crystal.
```crystal
require "kemal"
get "/" do
"Hello World!"
end
Kemal.run
```
```shell
crystal run src/your_app.cr
```
--------------------------------
### Define Multiple before_all Filters in Kemal.cr
Source: https://kemalcr.com/guide
This example shows how to define multiple `before_all` filters in Kemal.cr. These filters are executed in the order they are defined, allowing for sequential setup like authorization checks and session initialization.
```crystal
before_all do |env|
raise "Unauthorized" unless authorized?(env)
end
before_all do |env|
env.session = Session.new(env.cookies)
end
get "/foo" do |env|
"foo"
end
```
--------------------------------
### Initialize and Install Kemal Dependencies
Source: https://kemalcr.com/guide
Commands to initialize a new Crystal application, add Kemal as a dependency in shard.yml, and install the required packages.
```shell
crystal init app your_app
cd your_app
```
```yaml
dependencies:
kemal:
github: kemalcr/kemal
```
```shell
shards install
```
--------------------------------
### Deploy to Heroku
Source: https://kemalcr.com/guide
Configuration steps for deploying a Kemal app to Heroku, including Procfile setup, buildpack configuration, and environment variable management.
```bash
web: ./your_app --port $PORT --bind 0.0.0.0
heroku buildpacks:set https://github.com/crystal-lang/heroku-buildpack-crystal
git push heroku main
heroku config:set KEMAL_ENV=production
heroku addons:create heroku-postgresql:mini
heroku ps:scale web=1
```
```crystal
Kemal.config.port = ENV["PORT"]?.try(&.to_i) || 3000
Kemal.config.host_binding = ENV["HOST"]? || "0.0.0.0"
```
--------------------------------
### Testing Kemal Applications
Source: https://kemalcr.com/guide
Covers setting up spec-kemal dependencies and writing unit tests for Kemal routes.
```crystal
require "spec-kemal"
require "../src/your-kemal-app"
describe "Your::Kemal::App" do
it "renders /" do
get "/"
response.body.should eq "Hello World!"
end
end
```
--------------------------------
### Define HTTP Routes in Kemal
Source: https://kemalcr.com/guide
Examples of mapping different HTTP verbs (GET, POST, PUT, PATCH, DELETE) to specific route handlers in Kemal.
```crystal
# GET - Retrieve data
get "/" do
"Hello World!"
end
# POST - Create new resources
post "/" do
end
# PUT - Replace entire resource
put "/" do
end
# PATCH - Partially update resource
patch "/" do
end
# DELETE - Remove resources
delete "/" do
end
```
--------------------------------
### Automate Database Migrations
Source: https://kemalcr.com/guide
Scripts and SQL commands to handle database schema changes safely. Includes a bash wrapper for the Micrate tool and idempotent SQL migration examples.
```bash
#!/bin/bash
set -e
echo "Running database migrations..."
# Using Micrate (Crystal migration tool)
DATABASE_URL=$DATABASE_URL ./bin/micrate up
if [ $? -eq 0 ]; then
echo "Migrations completed successfully"
else
echo "Migration failed!"
exit 1
fi
```
```sql
-- migrations/001_add_users_table.sql
-- Always use IF NOT EXISTS for safety
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- migrations/002_add_index.sql
-- Create indexes concurrently (PostgreSQL)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users(email);
```
--------------------------------
### Application Logging Examples in Crystal
Source: https://kemalcr.com/guide
Shows examples of how to use Crystal's built-in `Log` module for application-level logging. It demonstrates logging messages at different severity levels: info, warn, and error. These logs can be used to track user activity, potential issues, and critical failures within the application.
```crystal
# Use Crystal's Log
Log.info { "User #{user_id} logged in" }
Log.warn { "Rate limit exceeded for IP #{ip}" }
Log.error { "Database connection failed: #{error}" }
```
--------------------------------
### Configuring Kemal Server and Security
Source: https://kemalcr.com/guide
Demonstrates how to configure server host/port, request body size limits, and static file directories.
```crystal
Kemal.config.host_binding = "127.0.0.1"
Kemal.config.port = 8080
Kemal.config.max_request_body_size = 1024 * 1024 * 10
Kemal.config.public_folder = "./assets"
```
--------------------------------
### Implementing WebSockets in Kemal
Source: https://kemalcr.com/guide
Demonstrates how to define WebSocket routes, handle messages, manage connections, and access HTTP context or URL parameters.
```crystal
ws "/" do |socket|
socket.send "Hello from Kemal!"
socket.on_message do |message|
socket.send "Echo back from server #{message}"
end
socket.on_close do
puts "Closing socket"
end
end
ws "/" do |socket, context|
headers = context.request.headers
socket.send headers["Content-Type"]?
end
ws "/:id" do |socket, context|
id = context.ws_route_lookup.params["id"]
end
```
--------------------------------
### Render ECR Views and Layouts
Source: https://kemalcr.com/guide
Demonstrates rendering ECR templates, using layouts, and utilizing content_for/yield_content for dynamic view composition.
```crystal
get "/:name" do |env|
name = env.params.url["name"]
render "src/views/hello.ecr", "src/views/layouts/layout.ecr"
end
# Inside index.ecr
<% content_for "some_key" do %>
...
<% end %>
# Inside layout.ecr
<%= yield_content "some_key" %>
```
--------------------------------
### Apply before_all Filter to Multiple HTTP Methods in Kemal.cr
Source: https://kemalcr.com/guide
This example illustrates the use of `before_all` in Kemal.cr, which applies a filter to all HTTP methods for a given path. Here, it sets the `Content-Type` to `application/json` for GET, PUT, and POST requests to '/foo'.
```crystal
before_all "/foo" do |env|
puts "Setting response content type"
env.response.content_type = "application/json"
end
get "/foo" do |env|
puts env.response.headers["Content-Type"] # => "application/json"
{"name": "Kemal"}.to_json
end
put "/foo" do |env|
puts env.response.headers["Content-Type"] # => "application/json"
{"name": "Kemal"}.to_json
end
post "/foo" do |env|
puts env.response.headers["Content-Type"] # => "application/json"
{"name": "Kemal"}.to_json
end
```
--------------------------------
### Build Production Binaries
Source: https://kemalcr.com/guide
Commands to compile Kemal applications for production, including options for release optimization and static linking for portability.
```bash
crystal build --release --no-debug src/your_app.cr
```
```bash
crystal build --release --static --no-debug src/your_app.cr
```
--------------------------------
### Run Kemal with SSL
Source: https://kemalcr.com/guide
Instructions for building and executing a Kemal application with SSL support using command-line flags for key and certificate files.
```bash
crystal build --release src/your_app.cr
./your_app --ssl --ssl-key-file your_key_file --ssl-cert-file your_cert_file
```
--------------------------------
### Implement Redis Caching
Source: https://kemalcr.com/guide
Shows how to cache expensive database queries using Redis to reduce latency and database load.
```crystal
require "redis"
REDIS = Redis.new(url: ENV["REDIS_URL"])
get "/popular-posts" do |env|
cache_key = "popular_posts"
cached = REDIS.get(cache_key)
if cached
env.response.content_type = "application/json"
next cached
end
posts = DB.query_all("SELECT * FROM posts ORDER BY views DESC LIMIT 10", as: Post)
result = posts.to_json
REDIS.setex(cache_key, 300, result)
env.response.content_type = "application/json"
result
end
```
--------------------------------
### Example .env File for Kemal.cr
Source: https://kemalcr.com/guide
Provides a sample .env file to define environment-specific configurations for a Kemal.cr application. This includes settings for the application environment, port, database URL, Redis URL, and a secret key base. It's recommended to use a .env.example file for version control and a .env file for local development secrets.
```env
KEMAL_ENV=production
PORT=3000
HOST=0.0.0.0
DATABASE_URL=postgres://user:password@localhost/myapp
REDIS_URL=redis://localhost:6379
SECRET_KEY_BASE=generate-a-secure-random-string-here
```
--------------------------------
### Handle HTTP Requests and Parameters in Kemal
Source: https://kemalcr.com/guide
Demonstrates how to define routes, extract query parameters, and parse JSON request bodies using the Kemal framework.
```crystal
get "/resize" do |env|
width = env.params.query["width"]
height = env.params.query["height"]
end
post "/json_params" do |env|
name = env.params.json["name"].as(String)
likes = env.params.json["likes"].as(Array)
"#{name} likes #{likes.join(',')}"
end
```
--------------------------------
### Manage HTTP Responses and Headers
Source: https://kemalcr.com/guide
Shows how to send JSON, HTML, and XML responses, set custom status codes, and manipulate response headers.
```crystal
get "/users" do |env|
env.json({users: ["alice", "bob"]})
end
post "/users" do |env|
env.status(:created).json({id: 1, created: true})
end
get "/headers" do |env|
env.response.headers["Accept-Language"] = "tr"
env.response.headers["Authorization"] = "Token 12345"
end
get "/status-code" do |env|
env.response.status_code = 404
end
```
--------------------------------
### Configure Kemal Application Settings
Source: https://kemalcr.com/guide
Demonstrates the two equivalent ways to configure Kemal options: using shorthand helper methods or the explicit Kemal.config object.
```crystal
logging false
public_folder "./assets"
serve_static false
```
```crystal
Kemal.config.logging = false
Kemal.config.public_folder = "./assets"
Kemal.config.serve_static = false
```
--------------------------------
### Configure DigitalOcean App Platform
Source: https://kemalcr.com/guide
Configuration for deploying a containerized Kemal application on DigitalOcean using the app.yaml specification.
```yaml
name: my-kemal-app
services:
- name: web
dockerfile_path: Dockerfile
github:
repo: username/repo
branch: main
deploy_on_push: true
health_check:
http_path: /health
http_port: 3000
instance_count: 1
instance_size_slug: basic-xxs
routes:
- path: /
envs:
- key: KEMAL_ENV
value: production
- key: DATABASE_URL
scope: RUN_TIME
type: SECRET
databases:
- name: db
engine: PG
production: false
```
--------------------------------
### Session Management with kemal-session in Kemal.cr
Source: https://kemalcr.com/guide
Demonstrates user login, protected routes, and logout using the `kemal-session` library. It includes session configuration with a secret key, setting session variables for username and login status, and destroying the session upon logout. This example uses the default `MemoryEngine` for session storage, which is suitable for development and testing.
```crystal
# User Login / Logout Example
require "kemal"
require "kemal-session"
# Session Configuration
Kemal::Session.config.secret = "my-secret-key"
# User login (create session)
post "/login" do |env|
username = env.params.body["username"]?.to_s
# In a real app you would authenticate here
env.session.string("username", username)
env.session.bool("logged_in", true)
"Welcome #{username}, you're now logged in."
end
# Protected route using session
get "/profile" do |env|
unless env.session.bool?("logged_in")
env.response.status_code = 401
next "Please log in first"
end
username = env.session.string("username")
"Hello #{username}!"
end
# User logout (destroy session)
post "/logout" do |env|
env.session.destroy
"You have been logged out."
end
Kemal.run
```
--------------------------------
### Use Custom Macro Renderer in Kemal.cr Route
Source: https://kemalcr.com/guide
This example shows how to use the custom `my_renderer` macro within a Kemal.cr route definition. It defines a GET route for '/:name' that utilizes the renderer to serve a subview.
```crystal
get "/:name" do
my_renderer "subview"
end
```
--------------------------------
### Define Custom Error Pages in Kemal.cr
Source: https://kemalcr.com/guide
This example shows how to define custom error pages in Kemal.cr using the `error` handler. It allows specifying custom responses for specific HTTP status codes like 404 and 403.
```crystal
error 404 do
"This is a customized 404 page."
end
error 403 do
"Access Forbidden!"
end
```
--------------------------------
### Configure Nginx HTTP/2 and Keep-Alive
Source: https://kemalcr.com/guide
Optimizes Nginx server settings by enabling HTTP/2 and configuring keep-alive timeouts for persistent connections.
```nginx
listen 443 ssl http2;
listen [::]:443 ssl http2;
keepalive_timeout 65;
keepalive_requests 100;
```
--------------------------------
### Halt Execution with Custom Status and Response in Kemal.cr
Source: https://kemalcr.com/guide
This example shows how to halt the execution of a Kemal.cr route using `halt`. It allows specifying a custom HTTP status code and response body, useful for immediate error responses.
```crystal
halt env, status_code: 403, response: "Forbidden"
```
--------------------------------
### Configure Database Connection Pooling
Source: https://kemalcr.com/guide
Demonstrates how to configure connection pooling for PostgreSQL using query parameters in the connection URI within a Kemal application.
```crystal
require "db"
require "pg"
DB = DB.open(ENV["DATABASE_URL"]? || "postgres://localhost/myapp?initial_pool_size=5&max_pool_size=25&max_idle_pool_size=10&checkout_timeout=5&retry_attempts=3&retry_delay=1")
get "/users" do |env|
users = DB.query_all("SELECT * FROM users", as: User)
users.to_json
end
```
--------------------------------
### Configure Nginx for Static Files and Gzip
Source: https://kemalcr.com/guide
Configures Nginx to serve static assets with caching headers and enables Gzip compression for improved performance.
```nginx
location /assets/ {
alias /opt/myapp/public/assets/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript application/xml+rss
application/rss+xml font/truetype font/opentype
application/vnd.ms-fontobject image/svg+xml;
gzip_disable "msie6";
```
--------------------------------
### Cross-Compile Kemal Applications
Source: https://kemalcr.com/guide
Provides methods for cross-compiling Crystal applications for Linux, including manual compilation and Docker-based build strategies.
```bash
# Manual cross-compile
crystal build --cross-compile --target x86_64-unknown-linux-gnu src/your_app.cr
# Docker-based build
docker run --rm -v $(pwd):/app -w /app crystallang/crystal:latest \
crystal build --release --static --no-debug src/your_app.cr -o bin/app-linux
# Multi-platform Docker build
docker buildx create --use
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .
```
--------------------------------
### Register Global and Path-Specific Middleware
Source: https://kemalcr.com/guide
Demonstrates registering middleware globally for all routes and path-specifically for routes matching a given prefix. The `use` keyword is used for registration, accepting single handlers or arrays.
```crystal
require "kemal"
# Path-specific middlewares for /api routes
use "/api", [CORSHandler.new, AuthHandler.new]
get "/" do
"Public home"
end
get "/api/users" do |env|
env.json({users: ["alice", "bob"]})
end
Kemal.run
```
--------------------------------
### Manage Nginx Service and SSL Certificates
Source: https://kemalcr.com/guide
Commands to enable the Nginx site configuration, verify syntax, and automate SSL certificate management using Certbot.
```bash
# Enable site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo mkdir -p /var/www/certbot
sudo certbot --nginx -d example.com -d www.example.com
# Renewal
sudo certbot renew --dry-run
sudo systemctl status certbot.timer
```
--------------------------------
### Manage Systemd Service for Kemal
Source: https://kemalcr.com/guide
Instructions and configuration for running a Kemal binary as a background service on Linux. Includes service file definition and management commands.
```ini
[Unit]
Description=Kemal Application
After=network.target postgresql.service
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/your_app
Restart=always
RestartSec=10
Environment=KEMAL_ENV=production
Environment=PORT=3000
Environment=HOST=127.0.0.1
EnvironmentFile=/opt/myapp/.env
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/myapp/log /opt/myapp/tmp
LimitNOFILE=65535
LimitNPROC=4096
[Install]
WantedBy=multi-user.target
```
--------------------------------
### Deploy to Railway
Source: https://kemalcr.com/guide
Commands to initialize and deploy a Kemal application using the Railway CLI, including adding managed database and cache services.
```bash
npm i -g @railway/cli
railway login
railway init
railway up
railway add postgres
railway add redis
```
--------------------------------
### Handle Custom Errors Based on Raised Exceptions in Kemal.cr
Source: https://kemalcr.com/guide
This example demonstrates how to handle custom errors in Kemal.cr based on raised exceptions. It defines an error handler for a specific exception type (`ValueError`) that will be triggered if that exception occurs within a route.
```crystal
get "/" do |env|
if some_condition
raise ValueError.new
end
{"message": "Hello Kemal"}.to_json
end
error ValueError do
"Something has gone wrong"
end
```
--------------------------------
### CI/CD Pipeline Configuration
Source: https://kemalcr.com/guide
Automated deployment pipelines for GitHub Actions and GitLab CI. These configurations handle testing, Docker image building, and remote server deployment.
```yaml
name: Deploy to Production
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Install Crystal
uses: crystal-lang/install-crystal@v1
with:
crystal: latest
- name: Install dependencies
run: shards install
- name: Run tests
run: crystal spec
env:
DATABASE_URL: postgres://postgres:postgres@localhost/test
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: $
password: $
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/$:latest
cache-from: type=gha
cache-to: type=gha,mode=max
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to production
uses: appleboy/ssh-action@master
with:
host: $
username: $
key: $
script: |
cd /opt/myapp
docker pull ghcr.io/$:latest
docker-compose up -d
docker system prune -f
```
```yaml
stages:
- test
- build
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
test:
stage: test
image: crystallang/crystal:latest
services:
- postgres:15
variables:
POSTGRES_DB: test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
DATABASE_URL: postgres://postgres:postgres@postgres/test
script:
- shards install
- crystal spec
only:
- main
- merge_requests
build:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
- docker tag $DOCKER_IMAGE $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
deploy:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache openssh-client
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan $SERVER_HOST >> ~/.ssh/known_hosts
script:
- |
ssh $SERVER_USER@$SERVER_HOST << EOF
cd /opt/myapp
docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
docker pull $DOCKER_IMAGE
docker-compose up -d
docker system prune -f
EOF
only:
- main
environment:
name: production
url: https://example.com
```
--------------------------------
### Enable SO_REUSEPORT for Multiple Kemal Instances
Source: https://kemalcr.com/guide
Configures the Kemal.cr server to enable the `SO_REUSEPORT` socket option. This allows multiple processes to bind to the same network port, which is essential for running multiple instances of the application on the same host and port for increased concurrency and fault tolerance. The example also shows how to identify the process ID within the response.
```crystal
require "kemal"
# Configure the server to reuse the port
Kemal.config.server.not_nil!.bind_tcp(
Kemal.config.host_binding,
Kemal.config.port,
reuse_port: true
)
# Your routes...
get "/" do
"Hello from process #{Process.pid}"
end
Kemal.run
```
--------------------------------
### Deploy to Fly.io
Source: https://kemalcr.com/guide
Configuration for deploying to Fly.io using a custom fly.toml file. This includes service definitions, health checks, and environment variable settings for production.
```toml
app = "my-kemal-app"
primary_region = "iad"
[build]
dockerfile = "Dockerfile"
[env]
KEMAL_ENV = "production"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
[[services]]
protocol = "tcp"
internal_port = 3000
[[services.ports]]
port = 80
handlers = ["http"]
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[checks]
[checks.health]
grace_period = "5s"
interval = "30s"
method = "get"
path = "/health"
timeout = "2s"
```
--------------------------------
### Configure KemalCR Environment Variables
Source: https://kemalcr.com/guide
Demonstrates how to load configuration from environment variables in a Crystal application using Kemal, ensuring sensitive data is not hardcoded.
```crystal
require "kemal"
Kemal.config.env = ENV["KEMAL_ENV"]? || "development"
Kemal.config.port = ENV["PORT"]?.try(&.to_i) || 3000
Kemal.config.host_binding = ENV["HOST"]? || "0.0.0.0"
DATABASE_URL = ENV["DATABASE_URL"]? || "postgres://localhost/myapp_dev"
SECRET_KEY = ENV["SECRET_KEY_BASE"]? || raise "SECRET_KEY_BASE is required"
ENABLE_FEATURE_X = ENV["ENABLE_FEATURE_X"]? == "true"
```
--------------------------------
### Accessing CSRF Token in Kemal
Source: https://kemalcr.com/guide
Shows how to include the CSRF token in an HTML form using the session object.
```html
">
```
--------------------------------
### Set Resource Limits and Security Headers
Source: https://kemalcr.com/guide
Configures basic security settings like request body size limits and disabling the 'Powered-By' header to improve application security.
```crystal
require "kemal"
# Maximum request body size (10 MB)
Kemal.config.max_request_body_size = 10 * 1024 * 1024
# Powered by header (hide for security)
Kemal.config.powered_by_header = false
# Always rescue in production
Kemal.config.always_rescue = true
```
--------------------------------
### Implement Security Headers with Helmet
Source: https://kemalcr.com/guide
Integrates the Helmet shard to automatically set various HTTP security headers, protecting the application from common web vulnerabilities.
```yaml
dependencies:
helmet:
github: EvanHahn/crystal-helmet
```
```crystal
require "kemal"
require "helmet"
# Add Helmet handlers (order matters – add early)
add_handler Helmet::DNSPrefetchControllerHandler.new
add_handler Helmet::FrameGuardHandler.new
add_handler Helmet::InternetExplorerNoOpenHandler.new
add_handler Helmet::NoSniffHandler.new
add_handler Helmet::StrictTransportSecurityHandler.new(7.day)
add_handler Helmet::XSSFilterHandler.new
get "/" do
"Hello World"
end
Kemal.run
```
--------------------------------
### Throttling and Blocking with Defense
Source: https://kemalcr.com/guide
Uses the Defense shard to throttle, block, or safelist requests based on IP addresses, providing protection against malicious traffic.
```yaml
dependencies:
defense:
github: defense-cr/defense
```
```crystal
require "kemal"
require "defense"
# Store: Redis (production) or MemoryStore (development/tests)
Defense.store = Defense::RedisStore.new(url: ENV["REDIS_URL"]? || "redis://localhost:6379/0")
add_handler Defense::Handler.new
# Throttle: 10 requests per minute per IP
Defense.throttle("requests per minute", limit: 10, period: 60) do |request|
request.remote_address.to_s
end
# Blocklist: block /admin for non-trusted IPs
Defense.blocklist("block admin") do |request|
request.path.starts_with?("/admin/")
end
# Safelist: never throttle/block localhost
Defense.safelist("localhost") do |request|
request.remote_address.to_s == "127.0.0.1"
end
get "/" do
"Hello World"
end
Kemal.run
```
--------------------------------
### Configure Nginx Reverse Proxy
Source: https://kemalcr.com/guide
Nginx configuration to act as a reverse proxy for a Kemal application, handling SSL termination and load balancing.
```nginx
upstream kemal {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$server_name$request_uri;
}
}
```
--------------------------------
### Configure Render Deployment
Source: https://kemalcr.com/guide
Defines the infrastructure requirements for a Kemal application on Render using a render.yaml configuration file. This includes service type, environment variables, and database integration.
```yaml
services:
- type: web
name: my-kemal-app
env: docker
plan: starter
dockerfilePath: ./Dockerfile
healthCheckPath: /health
envVars:
- key: KEMAL_ENV
value: production
- key: DATABASE_URL
fromDatabase:
name: myapp-db
property: connectionString
autoDeploy: true
databases:
- name: myapp-db
plan: starter
databaseName: myapp
user: myapp
```
--------------------------------
### Use Context Storage for Request State
Source: https://kemalcr.com/guide
Explains how to share data between filters and routes within a single request cycle using context storage, including support for custom types.
```crystal
before_get "/" do |env|
env.set "is_kemal_cool", true
end
get "/" do |env|
is_kemal_cool = env.get "is_kemal_cool"
"Kemal cool = #{is_kemal_cool}"
end
add_context_storage_type(User)
before "/" do |env|
env.set "user", User.new("dummy-user")
end
```
--------------------------------
### Build and Optimize Crystal Application
Source: https://kemalcr.com/guide
Commands to compile a Crystal application for production, including stripping symbols and enabling link-time optimization. These commands ensure the resulting binary is optimized for performance and size.
```bash
strip your_app
crystal build --release --no-debug -Dpreview_mt src/your_app.cr
KEMAL_ENV=production crystal build --release src/your_app.cr
```
--------------------------------
### Configure Extra Options with Argument Parser in Kemal
Source: https://kemalcr.com/guide
Extend Kemal's configuration by adding custom command-line options using an argument parser. This allows for flexible configuration loading and custom logic.
```crystal
Kemal.config.extra_options do |parser|
parser.on("-c CONFIG", "--config CONFIG", "Load configuration from file") do |config_file|
# Your custom logic here
end
end
```
--------------------------------
### Configure SSL/TLS for HTTPS in Kemal
Source: https://kemalcr.com/guide
Set up SSL/TLS to enable HTTPS for your Kemal application. This involves enabling the SSL configuration and providing paths to your certificate and key files.
```crystal
Kemal.config.ssl = true
Kemal.config.ssl_certificate_file = "/path/to/cert.pem"
Kemal.config.ssl_key_file = "/path/to/key.pem"
# Alternatively, use command line flags
# ./your_app --ssl --ssl-cert-file cert.pem --ssl-key-file key.pem
```
--------------------------------
### HTML Form for Method Override
Source: https://kemalcr.com/guide
Demonstrates how to use hidden input fields with the name `_method` in HTML forms to simulate PUT, PATCH, or DELETE requests when submitting via POST.
```html
```
--------------------------------
### Configure Docker Compose and Ignore Files
Source: https://kemalcr.com/guide
Configuration for local orchestration using Docker Compose and a .dockerignore file to exclude unnecessary build artifacts and source files from the image context.
```dockerignore
.git
.github
*.md
spec
lib
shard.lock
tmp
log
*.log
.env
.env.*
node_modules
.DS_Store
```
```yaml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
KEMAL_ENV: production
DATABASE_URL: postgres://postgres:password@db:5432/myapp
REDIS_URL: redis://redis:6379
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
```
--------------------------------
### Customize HTTP Server Instance in Kemal
Source: https://kemalcr.com/guide
Access and modify the underlying `HTTP::Server` instance used by Kemal. This allows for fine-grained control over network binding and other server-level settings.
```crystal
Kemal.config.server.not_nil!.bind_tcp "0.0.0.0", 3000, reuse_port: true
```
--------------------------------
### Configure Logging in Kemal
Source: https://kemalcr.com/guide
Enable, disable, or customize the logging behavior in Kemal. You can also add custom log messages or replace the default logger with your own implementation.
```crystal
# Disable logging
Kemal.config.logging = false # Default: true
# Add log messages
Log.info { "Log message with or without embedded #{variables}" }
# Use a custom logger
Kemal.config.logger = MyCustomLogger.new
```
--------------------------------
### Configure Log Rotation for KemalCR
Source: https://kemalcr.com/guide
A logrotate configuration file to manage application logs, preventing disk space exhaustion by rotating, compressing, and deleting old logs.
```text
/opt/myapp/log/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
true
endscript
}
```
--------------------------------
### Configure Systemd Service for Kemal
Source: https://kemalcr.com/guide
Defines a systemd service unit file to manage the lifecycle of a Kemal application. It specifies the environment, user permissions, and execution path for the application.
```ini
[Unit]
Description=Kemal Application
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/your_app
Restart=always
EnvironmentFile=/opt/myapp/.env
[Install]
WantedBy=multi-user.target
```
--------------------------------
### Configure Environment in Kemal
Source: https://kemalcr.com/guide
Set the application environment (e.g., 'development', 'production') using the `KEMAL_ENV` environment variable or `Kemal.config.env`. This affects error page rendering and other environment-specific behaviors.
```bash
# Set environment variable
$ export KEMAL_ENV=production
```
```crystal
# Set environment in code
Kemal.config.env = "production"
```
--------------------------------
### Accessing URL Parameters in Kemal
Source: https://kemalcr.com/guide
Utilize variables in route paths as placeholders for data. Access URL parameters using `env.params.url`. Supports dynamic segments (e.g., :name) and wildcard segments (e.g., *all).
```crystal
# Matches /hello/kemal
get "/hello/:name" do |env|
name = env.params.url["name"]
"Hello back to #{name}"
end
# Matches /users/1
get "/users/:id" do |env|
id = env.params.url["id"]
"Found user #{id}"
end
# Matches /dir/and/anything/after
get "/dir/*all" do |env|
all = env.params.url["all"]
"Found path #{all}"
end
```
--------------------------------
### Halt with Chained Response in Kemal.cr
Source: https://kemalcr.com/guide
This demonstrates a concise way to halt execution in Kemal.cr by chaining response modifications. It allows setting the status and HTML content directly before halting, suitable for API error handling.
```crystal
get "/admin" do |env|
halt env.status(403).html("
Forbidden
")
end
```
--------------------------------
### Send File with Default MIME Type
Source: https://kemalcr.com/guide
Sends a file using its extension to determine the MIME type. Defaults to 'application/octet-stream' if the extension is not recognized. The `env` object and the file path are required arguments.
```crystal
send_file env, "./path/to/file.jpg"
```
--------------------------------
### Conditional Middleware Execution with 'only'
Source: https://kemalcr.com/guide
Implements middleware that executes only for specific routes and HTTP methods. The `only` filter can take an array of paths and an optional HTTP method to define precise execution conditions.
```crystal
class OnlyHandler < Kemal::Handler
# Matches GET /specials and GET /deals
only ["/specials", "/deals"]
def call(env)
# continue on to next handler unless the request matches the only filter
return call_next(env) unless only_match?(env)
puts "If the path is /specials or /deals, I will be doing some processing here."
end
end
class PostOnlyHandler < Kemal::Handler
# Matches POST /blogs
only ["/blogs"], "POST"
def call(env)
# call_next is called for GET /blogs, but not POST /blogs
return call_next(env) unless only_match?(env)
puts "If the request is a POST to /blogs, I will do some processing here."
end
end
```
--------------------------------
### Set Response Content Type with before_get Filter in Kemal.cr
Source: https://kemalcr.com/guide
This example demonstrates using the `before_get` filter in Kemal.cr to modify the response before a GET request is handled. It sets the `Content-Type` header to `application/json` for the '/foo' route.
```crystal
before_get "/foo" do |env|
env.response.content_type = "application/json"
end
get "/foo" do |env|
puts env.response.headers["Content-Type"] # => "application/json"
{"name": "Kemal"}.to_json
end
```
--------------------------------
### Basic File Upload Handling in Kemal.cr
Source: https://kemalcr.com/guide
Handles a single file upload from a form field named 'image'. It saves the uploaded file to the Kemal.config.public_folder/uploads directory. This example does not include any validation.
```crystal
post "/upload" do |env|
# Get the uploaded file from the form field named "image"
file = env.params.files["image"].tempfile
# Create the destination path
file_path = ::File.join [Kemal.config.public_folder, "uploads/", File.basename(file.path)]
# Copy the uploaded file to the destination
File.open(file_path, "w") do |f|
IO.copy(file, f)
end
"Upload successful!"
end
```
--------------------------------
### Exception Handler Resolution Order in Kemal.cr
Source: https://kemalcr.com/guide
This example illustrates Kemal.cr's exception handler resolution order. Handlers are resolved first by definition order, then by inheritance order. A handler for a more general exception type will be chosen over a more specific one if defined earlier.
```crystal
class GrandParentException < Exception; end
class ParentException < GrandParentException; end
class ChildException < ParentException; end
error GrandParentException do
"Grandparent exception"
end
error ParentException do
"Parent exception"
end
get "/" do
raise ChildException.new()
end
```
--------------------------------
### Accessing Query Parameters in Kemal
Source: https://kemalcr.com/guide
Retrieve query parameters from the URL using `env.params.query`. This is commonly used for GET requests to pass optional filtering or sorting information.
```crystal
# Matches /resize?width=200&height=200
get "/resize" do |env|
width = env.params.query["width"]
height = env.params.query["height"]
end
```
--------------------------------
### Add Custom Handlers/Middleware in Kemal
Source: https://kemalcr.com/guide
Register custom middleware or handlers with your Kemal application. Handlers are executed in the order they are added for each incoming request.
```crystal
Kemal.config.add_handler MyCustomHandler.new
```
--------------------------------
### Zero-Downtime Deployment Script for Kemal.cr
Source: https://kemalcr.com/guide
A bash script (`scripts/deploy.sh`) that automates the process of deploying a new version of a Kemal.cr application with zero downtime. It builds the new release, copies the binary to the application directory, restarts each instance sequentially using systemd, and performs a health check after each restart to ensure the new version is operational before proceeding. This script ensures a smooth transition between application versions.
```bash
#!/bin/bash
set -e
APP_DIR="/opt/myapp"
PORTS=(3000 3001 3002)
echo "Building new version..."
crystal build --release --no-debug src/your_app.cr -o your_app.new
echo "Deploying with zero downtime..."
# Replace binary once (all instances share the same binary)
cp your_app.new $APP_DIR/your_app
for PORT in "${PORTS[@]}"; do
echo "Deploying to instance on port $PORT..."
# Restart instance (uses kemal-app@.service template)
sudo systemctl restart kemal-app@$PORT
# Wait for health check
sleep 5
# Check if healthy
if curl -f http://localhost:$PORT/health > /dev/null 2>&1; then
echo "Instance on port $PORT is healthy"
else
echo "Instance on port $PORT failed health check!"
exit 1
fi
# Wait before next instance
sleep 2
done
echo "Deployment complete!"
```
--------------------------------
### Configure 'X-Powered-By' Header in Kemal
Source: https://kemalcr.com/guide
Control the 'X-Powered-By' header sent by Kemal. You can disable it entirely or customize its value to something other than the default 'Kemal'.
```crystal
Kemal.config.powered_by_header = false # Disable header
Kemal.config.powered_by_header = "MyApp" # Custom value
# Default: "Kemal"
```
--------------------------------
### Containerize with Multi-Stage Dockerfile
Source: https://kemalcr.com/guide
A multi-stage Dockerfile configuration that builds the Crystal application in a builder image and copies the static binary to a minimal Alpine runtime image. This approach reduces image size and improves security.
```dockerfile
FROM crystallang/crystal:1.11.2-alpine AS builder
WORKDIR /app
COPY shard.yml shard.lock ./
RUN shards install --production
COPY . .
RUN crystal build --release --static --no-debug src/your_app.cr -o bin/app
FROM alpine:latest
WORKDIR /app
RUN apk add --no-cache libgcc
COPY --from=builder /app/bin/app .
COPY --from=builder /app/public ./public
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["./app"]
```
--------------------------------
### Nginx Upstream Configuration for Zero-Downtime Deployment
Source: https://kemalcr.com/guide
An Nginx configuration snippet that defines an upstream server group named 'kemal'. This setup allows Nginx to distribute incoming traffic across multiple Kemal application instances running on different ports (e.g., 3000, 3001, 3002). This is a fundamental part of achieving zero-downtime deployments by enabling load balancing and failover.
```nginx
upstream kemal {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
server 127.0.0.1:3002;
}
```
--------------------------------
### Systemd Service Template for Kemal Application Instances
Source: https://kemalcr.com/guide
A systemd service file template (`kemal-app@.service`) designed to manage individual Kemal application instances. It specifies the service description, dependencies, user, working directory, the executable command, and environment variables including the port number (`%i`) and an environment file. This allows for easy management and scaling of multiple application instances.
```systemd
[Unit]
Description=Kemal Application (port %i)
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/your_app
Restart=always
Environment=PORT=%i
EnvironmentFile=/opt/myapp/.env
[Install]
WantedBy=multi-user.target
```
--------------------------------
### Create Custom Middleware by Inheriting Kemal::Handler
Source: https://kemalcr.com/guide
Shows how to create a custom middleware by inheriting from `Kemal::Handler` and implementing the `call` method. The `call_next` method must be invoked to pass control to the subsequent middleware in the chain.
```crystal
class CustomHandler < Kemal::Handler
def call(context)
puts "Doing some custom stuff here"
call_next context
end
end
add_handler CustomHandler.new
```