ESC
Type to search...
S
Soli Docs

Controllers

Controllers are the heart of your application. They handle HTTP requests, process data, and determine the response. Soli supports both modern class-based controllers and simple function-based handlers.

Class-Based Controllers

Recommended for most applications. They provide structure, inheritance, and powerful hooks like before_action.

app/controllers/posts_controller.soli
class PostsController extends ApplicationController {
    static {
        this.layout = "layouts/posts";
    }

    fn index(req: Any) -> Any {
        let posts = Post.all();
        return render("posts/index", {
            "title": "All Posts",
            "posts": posts
        });
    }

    fn show(req: Any) -> Any {
        let id = req.params["id"];
        let post = Post.find(id);

        if post == null {
            return error(404, "Post not found");
        }

        return render("posts/show", {
            "title": post["title"],
            "post": post
        });
    }
}

Lifecycle Hooks

Execute code before or after actions. Perfect for authentication or data loading.

app/controllers/posts_controller.soli
class PostsController extends ApplicationController {
    static {
        // Run before ALL actions
        this.before_action = fn(req) {
            print("Processing: " + req.path);
            return req;
        };

        // Run before SPECIFIC actions only
        this.before_action(:show, :edit) = fn(req) {
            let post = Post.find(req.params["id"]);
            if post == null { return error(404, "Not Found"); }
            req["post"] = post; // Pass to action
            return req;
        };
    }
    
    // ... actions ...
}

Function-Based Controllers

Great for simple APIs or microservices where you don't need the full power of classes.

app/controllers/simple_controller.soli
// GET /health
fn health(req: Any) -> Any {
    return {
        "status": 200,
        "headers": {"Content-Type": "application/json"},
        "body": "{\"status\":\"ok\"}"
    };
}

The Request Object

Access all HTTP request details through the req parameter.

req.params

Route parameters & query strings

req.body

Raw request body content

req.headers

HTTP headers map

req.session

User session data storage

Response Types

Render View
return render("home/index", { "title": "Welcome" });
Redirect
return redirect("/login");
JSON API
return {
    "status": 200,
    "headers": { "Content-Type": "application/json" },
    "body": json_encode({ "data": posts })
};
Error
return error(404, "Page Not Found");

Pro Tip: Keep Controllers Thin

Delegate complex business logic to Models or Service objects. Controllers should primarily focus on handling the HTTP request and selecting the correct response.

Best Practices

  • Use class-based controllers for better organization and reuse.
  • Create a base ApplicationController to share logic like auth across all controllers.
  • Use before_action to load resources and DRY up your code.
  • Prefix private helper methods with _ to prevent them from becoming routes.

Next Steps