Routing
Routes map HTTP requests to controller actions. Define your application's URL structure in config/routes.sl.
HTTP Methods
Use these helpers to define routes for different HTTP methods:
| Method | Description | Example |
|---|---|---|
| get | Retrieve a resource | get("/users", "users#index") |
| post | Create a new resource | post("/users", "users#create") |
| put | Update a resource completely | put("/users/:id", "users#update") |
| patch | Update a resource partially | patch("/users/:id", "users#patch") |
| delete | Delete a resource | delete("/users/:id", "users#delete") |
Basic Routes
# Root page
get("/", "home#index");
# Static pages
get("/about", "home#about");
get("/contact", "home#contact");
# Form submissions
post("/contact", "home#submit_contact");
Route Parameters
Use :param_name to capture dynamic segments:
# Capture user ID
get("/users/:id", "users#show");
# Multiple parameters
get("/posts/:post_id/comments/:comment_id", "comments#show");
# Optional parameters
get("/users/:id?", "users#show");
Access parameters in your controller:
def show(req: Any) let user_id = req["params"]["id"];
let post_id = req["params"]["post_id"];
{
"status": 200,
"body": "User ID: " + user_id
}
end
Splat Routes
Use *param_name to capture the remaining path segments greedously:
# Capture remaining path
get("/files/*filepath", "files#serve");
get("/downloads/*filename", "downloads#handle");
# Combine with parameters
get("/users/:id/*action", "users#action");
get("/api/*version/users/*id", "api#user");
# Root splat
get("/*path", "catchall#handle");
Captured splat values include a leading slash:
def serve(req: Any) let filepath = req["params"]["filepath"];
# /files/docs/report.pdf -> filepath = "/docs/report.pdf"
# /files/images/photo.jpg -> filepath = "/images/photo.jpg"
{
"status": 200,
"body": "Serving: " + filepath
}
end
| Pattern | Request Path | Captured |
|---|---|---|
| /files/*path | /files/a/b/c | {path: "/a/b/c"} |
| /api/*version/users | /api/v1/users | {version: "/v1"} |
| /users/:id/*action | /users/123/edit | {id: "123", action: "/edit"} |
| /*splat | /any/path/here | {splat: "/any/path/here"} |
Wildcard Action Routes
Use controller#* to dynamically resolve actions from the URL path:
# Dynamic docs routes - /docs/routing → docs#routing
get("/docs/*", "docs#*");
# API routes with version - /api/v1/users → api#users
get("/api/*version/*action", "api#*");
# Files serving - /files/docs/readme → files#docs/readme
get("/files/*filepath", "files#serve");
The action name is resolved from captured splat parameters in this priority order:
splatparameterpath,filepath,filename,action, orresource- First non-nested parameter
class DocsController extends Controller
static
this.layout = "docs";
end
def routing(req: Any) let path = req["params"]["path"]; # "/routing"
render("docs/routing", {"active_page": "routing"})
end
def installation(req: Any) render("docs/installation", {"active_page": "installation"})
end
end
| Route Pattern | Request Path | Action Resolved |
|---|---|---|
| get("/docs/*", "docs#*") | /docs/routing | docs#routing |
| get("/docs/*", "docs#*") | /docs/installation | docs#installation |
| get("/api/*version/*action", "api#*") | /api/v1/users | api#users |
| get("/files/*filepath", "files#serve") | /files/docs/readme | files#docs/readme |
Query Parameters
Query strings are automatically parsed and available in req["query"]:
# URL: /search?q=solilang&page=1
def search(req: Any) let query = req["query"]["q"]; # "solilang"
let page = req["query"]["page"]; # "1"
{
"status": 200,
"body": "Searching for: " + query
}
end
RESTful Resources
Generate standard CRUD routes automatically with resources():
resources("users")
| Method | Path | Action |
|---|---|---|
| GET | /users | index |
| GET | /users/new | new |
| POST | /users | create |
| GET | /users/:id | show |
| GET | /users/:id/edit | edit |
| PUT | /users/:id | update |
| DELETE | /users/:id | delete |
Namespaces
Group routes under a common prefix, like /admin:
namespace("admin", ->
get("/dashboard", "admin#dashboard");
get("/users", "admin#users");
post("/users", "admin#create_user");
)
Scoped Middleware
Apply middleware only to specific route blocks:
# Only /admin/* routes require authentication
middleware("authenticate", ->
get("/admin", "admin#index");
get("/admin/users", "admin#users");
)
# Public routes - no auth needed
get("/", "home#index");
get("/about", "home#about");
Nested Resources
namespace("api", ->
resources("users", ->
resources("posts", ->
resources("comments")
)
)
)
# Creates: /api/users/:user_id/posts/:post_id/comments/:id
Auto-Derived Routes
Soli can automatically map routes to controllers based on naming conventions.
Defining a method named index in home_controller.sl automatically handles GET /home.