Skip to main content
Kit provides a flexible response system inspired by Laravel, allowing you to create JSON responses, text responses, redirects, and handle errors elegantly. The Response type enables using Rust’s ? operator for clean error propagation.

The Response Type

In Kit, controller handlers return a Response type, which is an alias for Result<HttpResponse, HttpResponse>:
pub type Response = Result<HttpResponse, HttpResponse>;
This design allows you to:
  • Return successful responses with Ok(HttpResponse::...)
  • Return error responses with Err(HttpResponse::...)
  • Use the ? operator for automatic error conversion

Creating Responses

JSON Responses

The most common response type. Use HttpResponse::json() or the json_response! macro:
use kit::{HttpResponse, Response, Request};
use serde_json::json;

pub async fn index(_req: Request) -> Response {
    // Using HttpResponse directly
    Ok(HttpResponse::json(json!({
        "users": [
            {"id": 1, "name": "John"},
            {"id": 2, "name": "Jane"}
        ]
    })))
}
Or with the convenient json_response! macro:
use kit::{json_response, Request, Response};

pub async fn index(_req: Request) -> Response {
    json_response!({
        "users": [
            {"id": 1, "name": "John"},
            {"id": 2, "name": "Jane"}
        ]
    })
}

Text Responses

For plain text responses:
use kit::{HttpResponse, Response, Request};

pub async fn health(_req: Request) -> Response {
    Ok(HttpResponse::text("OK"))
}
Or with the text_response! macro:
use kit::{text_response, Request, Response};

pub async fn health(_req: Request) -> Response {
    text_response!("OK")
}

Setting Status Codes

Chain .status() to set the HTTP status code:
use kit::{json_response, HttpResponse, Response, ResponseExt};

// On HttpResponse directly
pub async fn created(_req: Request) -> Response {
    Ok(HttpResponse::json(json!({"id": 1, "created": true}))
        .status(201))
}

// With macros using ResponseExt trait
pub async fn created_macro(_req: Request) -> Response {
    json_response!({"id": 1, "created": true})
        .status(201)
}

Adding Headers

Add custom headers with .header():
use kit::{HttpResponse, Response};

pub async fn download(_req: Request) -> Response {
    Ok(HttpResponse::text("file content")
        .header("Content-Disposition", "attachment; filename=\"data.txt\"")
        .header("X-Custom-Header", "value"))
}

Redirects

Kit provides two ways to create redirects:

Simple Redirects

Redirect to a specific URL or path:
use kit::{Redirect, Response};

pub async fn legacy(_req: Request) -> Response {
    Redirect::to("/new-path").into()
}

Named Route Redirects

Redirect to a named route using the redirect! macro (with compile-time route validation):
use kit::{redirect, Response};

pub async fn store(_req: Request) -> Response {
    // Create user...

    // Redirect to users.index route
    redirect!("users.index").into()
}

Redirects with Parameters

Add route parameters and query strings:
use kit::{redirect, Response};

pub async fn update(_req: Request) -> Response {
    // Update user...

    // Redirect to users.show with route parameter
    redirect!("users.show")
        .with("id", "123")
        .into()
}

pub async fn search(_req: Request) -> Response {
    // Redirect with query parameters
    redirect!("users.index")
        .query("page", "1")
        .query("sort", "name")
        .into()
}

Permanent Redirects

Use .permanent() for 301 redirects (default is 302):
use kit::{Redirect, Response};

pub async fn old_route(_req: Request) -> Response {
    Redirect::to("/new-route")
        .permanent()
        .into()
}

Error Handling

Kit automatically converts errors to appropriate HTTP responses when using the ? operator.

Using the ? Operator

Errors are automatically converted to JSON error responses:
use kit::{Request, Response};

pub async fn show(req: Request) -> Response {
    // This returns a 400 error if the parameter is missing
    let id = req.param("id")?;

    json_response!({
        "user_id": id
    })
}

AppError for Custom Errors

Use AppError for domain-specific errors with custom status codes:
use kit::{AppError, FrameworkError, Response};

pub async fn find_user(id: i32) -> Result<User, FrameworkError> {
    let user = db.find(id);

    if user.is_none() {
        return Err(AppError::not_found("User not found").into());
    }

    Ok(user.unwrap())
}

AppError Helper Methods

MethodStatus CodeUse Case
AppError::new(msg)500Generic server error
AppError::not_found(msg)404Resource not found
AppError::bad_request(msg)400Invalid request
AppError::unauthorized(msg)401Authentication required
AppError::forbidden(msg)403Access denied
AppError::unprocessable(msg)422Validation failed
AppError::conflict(msg)409Resource conflict

Custom Status Codes

Set any status code with .status():
use kit::AppError;

let error = AppError::new("Rate limited")
    .status(429);

FrameworkError Types

Kit’s FrameworkError handles common error scenarios:
use kit::FrameworkError;

// Service not found (500)
FrameworkError::service_not_found::<MyService>();

// Missing parameter (400)
FrameworkError::param("user_id");

// Validation error (422)
FrameworkError::validation("email", "Invalid email format");

// Database error (500)
FrameworkError::database("Connection failed");

// Internal error (500)
FrameworkError::internal("Unexpected error");

// Custom domain error
FrameworkError::domain("Custom error", 418);

Error Response Format

Errors are returned as JSON with appropriate structure:
// Parameter error (400)
{
  "error": "Missing required parameter: user_id"
}

// Validation error (422)
{
  "error": "Validation failed",
  "field": "email",
  "message": "Invalid email format"
}

// Generic error
{
  "error": "Error message here"
}

Implementing HttpError Trait

For custom error types, implement the HttpError trait:
use kit::HttpError;

#[derive(Debug)]
struct UserNotFoundError {
    user_id: i32,
}

impl std::fmt::Display for UserNotFoundError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "User {} not found", self.user_id)
    }
}

impl std::error::Error for UserNotFoundError {}

impl HttpError for UserNotFoundError {
    fn status_code(&self) -> u16 {
        404
    }
}

Response Macros

Kit provides convenient macros for creating responses:
MacroDescriptionExample
json_response!Create a JSON responsejson_response!({"key": "value"})
text_response!Create a text responsetext_response!("Hello")
redirect!Redirect to named routeredirect!("users.index").into()

Complete Example

use kit::{
    json_response, redirect, text_response,
    AppError, FrameworkError, HttpResponse,
    Request, Response, ResponseExt,
};

// List users
pub async fn index(_req: Request) -> Response {
    json_response!({
        "users": [
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"}
        ]
    })
}

// Show a specific user
pub async fn show(req: Request) -> Response {
    let id = req.param("id")?;

    // Simulate user lookup
    if id == "999" {
        return Err(HttpResponse::json(serde_json::json!({
            "error": "User not found"
        })).status(404));
    }

    json_response!({
        "id": id,
        "name": format!("User {}", id)
    })
}

// Create a user
pub async fn store(_req: Request) -> Response {
    // ... create user logic ...

    // Return 201 Created
    json_response!({"id": 1, "created": true})
        .status(201)
}

// Delete and redirect
pub async fn destroy(_req: Request) -> Response {
    // ... delete logic ...

    redirect!("users.index").into()
}

// Custom error example
pub async fn process(_req: Request) -> Response {
    let result = do_something()?;

    if !result.is_valid() {
        return Err(AppError::unprocessable("Invalid data").into());
    }

    json_response!({"success": true})
}

Summary

FeatureUsage
JSON responseHttpResponse::json(value) or json_response!({...})
Text responseHttpResponse::text(str) or text_response!(str)
Set status.status(code)
Add header.header(name, value)
Simple redirectRedirect::to(path).into()
Named redirectredirect!("route.name").into()
With route params.with("key", "value")
With query params.query("key", "value")
Permanent redirect.permanent()
Not found errorAppError::not_found(msg)
Bad requestAppError::bad_request(msg)
UnauthorizedAppError::unauthorized(msg)
Custom statusAppError::new(msg).status(code)