Kemal is a Fast, Effective, Simple web framework for Crystal.

Hello everyone,

Kemal 1.10.0 is here :tada: This is the biggest Kemal release to date: a modular router, a clearer way to plug in middleware, richer response helpers, and several reliability fixes across routing, the CLI, and request parsing :rocket:

Modular Kemal::Router :compass:

You can now build namespaced apps with scoped filters, WebSocket routes, and flexible mounting—while the classic top-level DSL stays fully supported.

require "kemal"

api = Kemal::Router.new

api.namespace "/users" do
  get "/" do |env|
    env.json({users: ["alice", "bob"]})
  end

  get "/:id" do |env|
    env.text "user #{env.params.url["id"]}"
  end
end

mount "/api/v1", api

Kemal.run

This makes it easier to organize large APIs, version routes, and reuse sub-applications without losing Kemal’s familiar style.

Register global or path-specific middleware in one place. You can pass a single handler, an array of handlers, and control where they sit in the chain.

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

Chainable response helpers :art:

Response helpers are chainable for JSON, HTML, text, and XML. They understand HTTP::Status (including symbols like :created), and you can halt with a fully built response—handy for short API error paths.

require "kemal"

get "/users" do |env|
  env.json({users: ["alice", "bob"]})
end

post "/users" do |env|
  env.status(:created).json({id: 1, created: true})
end

get "/admin" do |env|
  halt env.status(403).html("<h1>Forbidden</h1>")
end

get "/api/users" do |env|
  env.json({data: ["alice", "bob"]}, content_type: "application/vnd.api+json")
end

Kemal.run

Filters and routing correctness :white_check_mark:

  • Wildcard filters: Global wildcard filters now always run as intended, while namespace filters stay scoped to their routes—so behavior matches what you expect in modular apps.
  • Route LRU cache: The route lookup cache is now concurrency-safe thanks to a Mutex, which matters under parallel request handling.
  • OverrideMethodHandler: Fixed a route cache bug when using the _method parameter for HTTP verb override (e.g. in HTML forms).

CLI and request body :wrench:

  • CLI: SSL validation and option parsing were tightened, with expanded specs around CLI behavior.
  • ParamParser#raw_body: Handlers can read the raw body multiple times when needed (for example alongside kemal-session), while still using parsed body params elsewhere.
post "/" do |env|
  raw = env.params.raw_body  # raw body, multiple handlers can call it
  env.params.body["name"]    # parsed body
end

Thanks for using and supporting Kemal. Full details and links to the related pull requests are in the v1.10.0 release notes.

P.S: You can support Kemal development via Github Sponsors :pray:

Happy Crystalling :heart: