struct

Kemal::Controller

Inherits Struct / Value / Object

Abstract controller class that provides a structured way to define HTTP endpoints.

Controllers are structs, so the overhead is minimal and you can still use all Kemal features. Method parameters automatically map to GET/POST/URL parameters with type-safe conversion.

Example

struct UsersController < Kemal::Controller
  @[Get("/users")]
  def index
    "Listing all users"
  end

  @[Get("/users/:id")]
  def show(id : Int32)
    "Showing user with ID: #{id}"
  end

  @[Post("/users")]
  def create(name : String, age : Int32, description : String?)
    "Creating user with name: #{name}, age: #{age}, description: #{description}"
  end
end

Supported Parameter Types

  • String
  • Int32, Int64
  • Bool
  • Array (with nested support)
  • NamedTuple (with nested support)
  • Nilable versions of the above

Parameter Mapping

  • name=John becomes name : String
  • item[foo]=bar becomes item : NamedTuple(foo: String)
  • items[]=1&items[]=2 becomes items : Array(Int32)
  • items[][id]=1&items[][quantity]=2 becomes items : Array(NamedTuple(id: Int32, quantity: Int32))

Route annotation parameters

  • path : String - The URL path for the route (can include path parameters like :id)
  • auth : Bool - If true, requires authentication via authenticate! method (default: false)
  • strip : Bool | Array(Symbol) - If true, strips all parameters; if array, strips only specified parameters (default: false)

Example

@[Get("/users/:id")]
def show(id : Int32)
  "User #{id}"
end

Example with Authentication

@[Get("/admin/dashboard", auth: true)]
def dashboard
  "Admin Dashboard"
end

def authenticate! : Bool
  # Return false to halt with 401 status
  request.headers["Authorization"]? == "SecretToken"
end

Example with Parameter Stripping

@[Post("/users", strip: true)]
def create(name : String, description : String?)
  # name and description will have leading/trailing whitespace removed
end

@[Post("/login", strip: [:email])]
def login(email : String, password : String)
  # Only email will be stripped, password remains unchanged
end

Constructors

new(context : HTTP::Server::Context, socket : HTTP::WebSocket | Nil = nil)

Initializes a new controller instance.

This is called automatically by the framework when processing a request. You typically don't need to call this directly.

Parameters

  • context : HTTP::Server::Context - The HTTP server context for the request
  • socket : HTTP::WebSocket? - The WebSocket connection, only set for @[WebSocket] methods
Source

Instance methods

close(*args, **options)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
close(*args, **options, &)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
context

The HTTP server context for the current request.

Provides access to the underlying HTTP::Server::Context which contains the request and response objects.

Source
error(field, message, status : HTTP::Status | Nil = nil)

Adds a field-specific error message.

Stores an error message for a specific field and sets the appropriate HTTP status code. If no custom status is provided, sets 400 (Bad Request) for GET/HEAD/OPTIONS requests or 422 (Unprocessable Entity) for POST/PUT/PATCH/DELETE requests.

Parameters

  • field : String - The name of the field that has an error
  • message : String - The error message for this field
  • status : HTTP::Status? - Optional custom HTTP status code (default: nil)

Example

def create(email : String, password : String)
  if !email.includes?("@")
    error("email", "Invalid email format")
    render("src/views/users/new.ecr")
    return
  end
  if password.size < 8
    error("password", "Password must be at least 8 characters", HTTP::Status::BAD_REQUEST)
    render("src/views/users/new.ecr")
    return
  end
end
Source
error(message : String)

Adds a general error message to the base error field.

This is useful for errors that don't belong to a specific field. Sets the response status to 400 (Bad Request) for GET/HEAD/OPTIONS requests or 422 (Unprocessable Entity) for POST/PUT/PATCH/DELETE requests.

Parameters

  • message : String - The error message to add

Example

def create(name : String)
  if name.empty?
    error("Name cannot be empty")
    render("src/views/users/new.ecr")
    return
  end
end
Source
error_for?(field : String) : String | Nil

Returns the error message for a specific field.

Returns nil if there is no error for the specified field.

Parameters

  • field : String - The name of the field to check for errors

Example

def create(email : String)
  error("email", "Invalid email") unless email.includes?("@")

  if msg = error_for?("email")
    render("src/views/users/new.ecr")
    return
  end
end
Source
error_for_base

Returns the error message for the "base" field.

The "base" field is used for general errors that don't belong to a specific field. Returns nil if there is no base error.

Example

def update
  error("Something went wrong")
  if msg = error_for_base
    render("src/views/error.ecr")
    return
  end
end
Source
errors

Hash of validation errors that occurred during request processing.

Maps field names to error messages. Use error methods to add errors and has_error?, error_for?, error_for_base to check for errors.

Returns nil if no errors have been recorded.

Source
has_error?

Checks if any errors have been recorded.

Returns true if there are one or more validation errors, false otherwise.

Example

def create(name : String, email : String)
  error("name", "Name is required") if name.empty?
  error("email", "Email is required") if email.empty?

  if has_error?
    render("src/views/users/new.ecr")
    return
  end

  # Process the valid data
end
Source
on_binary

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source
on_close

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source
on_message

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source
on_ping

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source
on_pong

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source
ping(*args, **options)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
ping(*args, **options, &)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
pong(*args, **options)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
pong(*args, **options, &)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
redirect(*args, **options)

Delegates to the redirect method from the context.

Redirects the request to another URL.

Example

redirect("/login")
Source
redirect(*args, **options, &)

Delegates to the redirect method from the context.

Redirects the request to another URL.

Example

redirect("/login")
Source
request(*args, **options)

Delegates to the request object from the context.

Provides direct access to the HTTP::Request for the current request.

Source
request(*args, **options, &)

Delegates to the request object from the context.

Provides direct access to the HTTP::Request for the current request.

Source
response(*args, **options)

Delegates to the response object from the context.

Provides direct access to the HTTP::Response for the current request.

Source
response(*args, **options, &)

Delegates to the response object from the context.

Provides direct access to the HTTP::Response for the current request.

Source
send(*args, **options)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
send(*args, **options, &)

Delegates the non-block WebSocket methods to the socket getter.

Lets @[WebSocket] methods call send, close, etc. directly instead of going through socket. Like socket, these raise NilAssertionError if called from a regular HTTP route handler.

Source
session(*args, **options)

Delegates to the session object from the context.

Provides access to the Kemal session for the current request.

Source
session(*args, **options, &)

Delegates to the session object from the context.

Provides access to the Kemal session for the current request.

Source
socket

The WebSocket connection for the current request.

Only available inside methods annotated with @[WebSocket]. Raises NilAssertionError if accessed from a regular HTTP route handler.

Source
socket?

The WebSocket connection for the current request.

Only available inside methods annotated with @[WebSocket]. Raises NilAssertionError if accessed from a regular HTTP route handler.

Source
stream(binary = true, frame_size = 1024, &)

Forwards the block-accepting WebSocket methods to the socket getter.

These can't be handled by delegate because the target methods capture their block (&), and the wrapper delegate generates would yield from inside a captured block, which doesn't compile. Like socket, they raise NilAssertionError when called from a regular HTTP route handler.

Source

Nested types