dream/router
Route configuration and request matching
The router matches incoming requests to controllers based on HTTP method and path patterns. It supports path parameters, wildcards, middleware chains, and custom context/services types.
Basic Routing
import dream/router.{router}
import dream/http/transaction.{Get, Post}
pub fn create_router() {
router
|> router.route(Get, "/", controllers.index, [])
|> router.route(Get, "/users/:id", controllers.show_user, [])
|> router.route(Post, "/users", controllers.create_user, [])
}
Path Parameters
Use :name to capture path segments as parameters:
/users/:idmatches/users/123and extractsid = "123"/posts/:post_id/comments/:idextracts both parameters
Access parameters in your controller with get_param(request, "id").
Wildcards
Wildcards match one or more path segments:
*or*name- Matches exactly one segment**or**path- Matches zero or more segments (greedy)*.jpg- Matches any path ending in.jpg*.{jpg,png,gif}- Matches multiple extensions
Middleware
Middleware run before (and optionally after) your controller:
router
|> router.route(
Get,
"/admin/users",
controllers.admin_users,
[auth_middleware, logging_middleware]
)
Middleware are executed in order: auth → logging → controller → logging → auth.
Each middleware can modify the request on the way in or the response on the way out.
Route Matching
Routes are matched in the order they’re defined. First match wins. More specific routes should come before general ones:
router
|> router.route(Get, "/users/new", controllers.new_user, []) // Specific
|> router.route(Get, "/users/:id", controllers.show_user, []) // General
Types
Placeholder for when you haven’t defined services yet
Use this as your services type during initial development. Replace it with your own services type when you add database connections, caches, or other shared dependencies.
pub type EmptyServices {
EmptyServices
}
Constructors
-
EmptyServices
Middleware function wrapper
Middleware intercept requests before they reach controllers and responses before they’re sent back. They’re generic over context and services types so they can work with any application configuration.
pub type Middleware(context, services) {
Middleware(
fn(
request.Request,
context,
services,
fn(request.Request, context, services) -> response.Response,
) -> response.Response,
)
}
Constructors
-
Middleware( fn( request.Request, context, services, fn(request.Request, context, services) -> response.Response, ) -> response.Response, )
A single route definition
Combines an HTTP method, path pattern, controller function, and optional middleware. Routes are matched in the order they’re added to the router.
pub type Route(context, services) {
Route(
method: request.Method,
path: String,
controller: fn(request.Request, context, services) -> response.Response,
middleware: List(Middleware(context, services)),
)
}
Constructors
-
Route( method: request.Method, path: String, controller: fn(request.Request, context, services) -> response.Response, middleware: List(Middleware(context, services)), )
Router holding your application’s routes
The router maintains a list of routes and provides them to the server for request matching. It’s generic over context and services types so the type system can verify your whole app.
pub type Router(context, services) {
Router(routes: List(Route(context, services)))
}
Constructors
-
Router(routes: List(Route(context, services)))
Values
pub fn build_controller_chain(
middleware: List(Middleware(context, services)),
final_controller: fn(request.Request, context, services) -> response.Response,
) -> fn(request.Request, context, services) -> response.Response
Build a controller chain from middleware and final controller
Composes middleware with the controller to create a single function. Middleware execute in order on the way in, then in reverse order on the way out.
For middleware [auth, logging] with controller handle:
Request → auth → logging → handle → logging → auth → Response
pub fn controller(
route: Route(context, services),
controller_function: fn(request.Request, context, services) -> response.Response,
) -> Route(context, services)
Set the controller function for the route
pub fn find_route(
router: Router(context, services),
request: request.Request,
) -> option.Option(
#(Route(context, services), List(#(String, String))),
)
Find matching route and extract params
pub fn match_path(
pattern_string: String,
path: String,
) -> option.Option(List(#(String, String)))
Match a path against a pattern and extract parameters
Returns the extracted parameters if the path matches the pattern, or None if it doesn’t.
Path Parameters
match_path("/users/:id", "/users/123")
// -> Some([#("id", "123")])
match_path("/users/:user_id/posts/:id", "/users/123/posts/456")
// -> Some([#("user_id", "123"), #("id", "456")])
Wildcards
// Single-segment wildcard
match_path("/assets/*file", "/assets/logo.png")
// -> Some([#("file", "logo.png")])
// Multi-segment wildcard
match_path("/files/**path", "/files/docs/guide.pdf")
// -> Some([#("path", "docs/guide.pdf")])
// Extension matching
match_path("/images/*.jpg", "/images/photo.jpg")
// -> Some([])
// Multiple extensions
match_path("/images/*.{jpg,png}", "/images/photo.jpg")
// -> Some([])
pub fn method(
route: Route(context, services),
method_value: request.Method,
) -> Route(context, services)
Set the HTTP method for the route
pub fn middleware(
route: Route(context, services),
middleware_list: List(
fn(
request.Request,
context,
services,
fn(request.Request, context, services) -> response.Response,
) -> response.Response,
),
) -> Route(context, services)
Add middleware to the route (accepts a list for convenience)
pub const new: Route(context.AppContext, EmptyServices)
Default route constant with AppContext
pub fn path(
route: Route(context, services),
path_value: String,
) -> Route(context, services)
Set the path for the route
pub fn route(
router: Router(context, services),
method method_value: request.Method,
path path_value: String,
controller controller_function: fn(
request.Request,
context,
services,
) -> response.Response,
middleware middleware_list: List(
fn(
request.Request,
context,
services,
fn(request.Request, context, services) -> response.Response,
) -> response.Response,
),
) -> Router(context, services)
Add a route to the router
Routes are matched in the order they’re added, so put more specific routes first.
The path supports parameters (:id), wildcards (*, **), and extensions (*.jpg).
Examples
// Simple route
router.route(router, Get, "/", home_controller, [])
// Route with path parameter
router.route(router, Get, "/users/:id", show_user, [])
// Route with middleware
router.route(router, Post, "/admin/users", create_user, [auth, logging])
// Wildcard route for static files
router.route(router, Get, "/assets/**path", serve_static, [])