### 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 ```