Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Handler Macros

The SDK provides several macros for running handler loops with different patterns.

Quick Reference

MacroHandler SignatureUse Case
handler_loop!fn(Request) -> ResponseSimple sync handlers
handler_loop_result!fn(Request) -> Result<Response, HandlerError>Sync with error handling
handler_loop_async!async fn(Request) -> ResponseAsync handlers
handler_loop_async_result!async fn(Request) -> Result<Response, HandlerError>Async with error handling

Sync Handlers

handler_loop!

For simple synchronous handlers that return a Response directly.

#![allow(unused)]
fn main() {
use rust_edge_gateway_sdk::prelude::*;

fn handle(req: Request) -> Response {
    Response::ok(json!({"path": req.path, "method": req.method}))
}

handler_loop!(handle);
}

handler_loop_result!

For handlers that return Result<Response, HandlerError>. Errors are automatically converted to HTTP responses.

#![allow(unused)]
fn main() {
use rust_edge_gateway_sdk::prelude::*;

#[derive(Deserialize)]
struct CreateItem {
    name: String,
    price: f64,
}

fn handle(req: Request) -> Result<Response, HandlerError> {
    // These all use ? operator - errors become HTTP responses
    let auth = req.require_header("Authorization")?;
    let item: CreateItem = req.json()?;
    let id: i64 = req.require_path_param("id")?;
    
    if item.price < 0.0 {
        return Err(HandlerError::ValidationError("Price cannot be negative".into()));
    }
    
    Ok(Response::created(json!({"id": id, "item": item.name})))
}

handler_loop_result!(handle);
}

Async Handlers

Async handlers require the async feature to be enabled.

Cargo.toml

[dependencies]
rust-edge-gateway-sdk = { git = "https://github.com/user/rust-edge-gateway", features = ["async"] }
tokio = { version = "1", features = ["full"] }

handler_loop_async!

For async handlers that return a Response directly. A Tokio runtime is created automatically.

#![allow(unused)]
fn main() {
use rust_edge_gateway_sdk::prelude::*;

async fn handle(req: Request) -> Response {
    let data = fetch_from_api().await;
    Response::ok(data)
}

handler_loop_async!(handle);
}

handler_loop_async_result!

For async handlers with Result-based error handling. Combines async support with automatic error conversion.

#![allow(unused)]
fn main() {
use rust_edge_gateway_sdk::prelude::*;

async fn handle(req: Request) -> Result<Response, HandlerError> {
    let data: CreateItem = req.json()?;
    
    // Async database call
    let id = database.insert(&data).await
        .map_err(|e| HandlerError::DatabaseError(e.to_string()))?;
    
    // Async file upload
    let url = s3.upload(&data.file).await
        .map_err(|e| HandlerError::StorageError(e.to_string()))?;
    
    Ok(Response::created(json!({
        "id": id,
        "file_url": url
    })))
}

handler_loop_async_result!(handle);
}

Runtime Management

Important: The async macros create a single Tokio runtime that persists across all requests. This is more efficient than creating a runtime per request.

#![allow(unused)]
fn main() {
// DON'T do this - creates runtime per request (inefficient):
fn handle(req: Request) -> Response {
    let rt = tokio::runtime::Runtime::new().unwrap();
    rt.block_on(async {
        // async work
    })
}

// DO this - runtime is created once by the macro:
async fn handle(req: Request) -> Response {
    // async work directly
}
handler_loop_async!(handle);
}

Choosing the Right Macro

Your handler needs...Use this macro
Simple sync logichandler_loop!
Sync with ? operatorhandler_loop_result!
Async operations (DB, HTTP, files)handler_loop_async!
Async with ? operatorhandler_loop_async_result!

Example: Complete CRUD Handler

#![allow(unused)]
fn main() {
use rust_edge_gateway_sdk::prelude::*;

async fn handle(req: Request) -> Result<Response, HandlerError> {
    match (req.method.as_str(), req.path.as_str()) {
        ("GET", "/items") => list_items().await,
        ("POST", "/items") => create_item(&req).await,
        ("GET", _) if req.path.starts_with("/items/") => get_item(&req).await,
        ("DELETE", _) if req.path.starts_with("/items/") => delete_item(&req).await,
        _ => Err(HandlerError::MethodNotAllowed("Use GET, POST, or DELETE".into())),
    }
}

async fn list_items() -> Result<Response, HandlerError> {
    let items = db::get_all_items().await
        .map_err(|e| HandlerError::DatabaseError(e.to_string()))?;
    Ok(Response::ok(json!({"items": items})))
}

async fn create_item(req: &Request) -> Result<Response, HandlerError> {
    let item: NewItem = req.json()?;
    let id = db::insert_item(&item).await
        .map_err(|e| HandlerError::DatabaseError(e.to_string()))?;
    Ok(Response::created(json!({"id": id})))
}

handler_loop_async_result!(handle);
}