dream/dream
Core Dream server type and shared functionality
This module provides the generic Dream server type and core routing functionality that is shared across all server implementations.
Most applications will use dream/servers/mist/server instead of this
module directly. This module contains the core types and routing logic
that the server implementations build upon.
Types
Generic Dream server type
Represents a configured Dream web server, parameterized over:
server: The underlying server implementation (e.g., Mist)context: Your application’s context typeservices: Your application’s services type
This type holds all the configuration needed to run a web server: router, context template, services, and server-specific settings.
Fields
server: The underlying HTTP server (Mist Builder)router: Optional router with all routes configuredcontext: Template context cloned for each requestservices: Optional services instance (database, cache, etc.)max_body_size: Maximum allowed request body size in bytes
Type Parameters
The Dream type is generic over three parameters:
- server: The underlying server type (usually Mist)
- context: Your application’s context type
- services: Your application’s services type
This allows the type system to verify that your router, controllers, and middleware all use compatible types.
Example
import dream/servers/mist/server as dream
import mist.{type Connection, type ResponseData}
// Your application types
pub type MyContext {
MyContext(request_id: String, user: Option(User))
}
pub type Services {
Services(db: Connection, cache: Cache)
}
// Dream server with your types
// Type: Dream(mist.Builder(Connection, ResponseData), MyContext, Services)
let server =
dream.new()
|> dream.context(MyContext(request_id: "", user: None))
|> dream.services(Services(db: my_db, cache: my_cache))
|> dream.router(my_router)
pub opaque type Dream(server, context, services)
Values
pub fn create(
server server: server,
router router: option.Option(router.Router(context, services)),
context context: context,
services services: option.Option(services),
max_body_size max_body_size: Int,
bind_interface bind_interface: option.Option(String),
) -> Dream(server, context, services)
Create a new Dream instance with explicit configuration
⚠️ Warning: This is a low-level constructor
This function is primarily for internal Dream framework use. Most users should
use the builder pattern via dream/servers/mist/server.new() instead.
When You Might Need This
You might use this if:
- You’re building custom server adapters for Dream
- You’re testing Dream internals
- You need to construct a Dream instance programmatically with all fields at once
Better Alternative
Use the builder pattern for better readability and type safety:
server.new()
|> server.context(MyContext)
|> server.services(MyServices)
|> server.router(my_router)
|> server.max_body_size(10_000_000)
|> server.bind("0.0.0.0")
pub fn execute_route(
route: router.Route(context, services),
request: request.Request,
params: List(#(String, String)),
context: context,
services: services,
) -> response.Response
Execute a route with its params, middleware, and controller
Helper function to execute a route that’s already been matched. Used by route_request and can be called directly when route is already known (e.g., in server handlers that need to find the route first to determine if it’s streaming).
Parameters
route: The matched route to executerequest: HTTP request (may have body/stream already attached)params: Path parameters extracted from the route patterncontext: Application contextservices: Application services
Returns
HTTP response from the controller
pub fn get_bind_interface(
dream: Dream(server, context, services),
) -> option.Option(String)
Get the bind interface configured for this Dream instance
Returns the network interface the server will bind to if configured, or None if using the default interface.
Example
let app = server.new() |> server.bind("0.0.0.0")
case dream.get_bind_interface(app) {
Some(interface) -> // Will bind to specified interface
None -> // Will use default interface
}
pub fn get_context(
dream: Dream(server, context, services),
) -> context
Get the context configured for this Dream instance
Returns the context value that will be passed to all controllers.
Example
let app = server.new() |> server.context(MyContext(user: None))
let ctx = dream.get_context(app)
pub fn get_max_body_size(
dream: Dream(server, context, services),
) -> Int
Get the max body size configured for this Dream instance
Returns the maximum request body size in bytes. Requests with bodies larger than this will be rejected.
Example
let app = server.new() |> server.max_body_size(5_000_000)
dream.get_max_body_size(app) // Returns 5_000_000
pub fn get_router(
dream: Dream(server, context, services),
) -> option.Option(router.Router(context, services))
Get the router configured for this Dream instance
Returns the router if one has been set, or None if not yet configured.
Example
let app = server.new() |> server.router(my_router)
case dream.get_router(app) {
Some(router) -> // Router is configured
None -> // No router yet
}
pub fn get_server(
dream: Dream(server, context, services),
risks_understood risks: Bool,
) -> server
Get the underlying server instance from a Dream instance
⚠️ Warning: This breaks Dream’s server abstraction
This function exposes the underlying server implementation (currently mist.Builder).
Using this in your application code creates tight coupling to Mist and prevents
Dream from switching server implementations in the future.
The risks_understood Parameter
You must explicitly pass risks_understood: True to call this function.
Passing False will panic. This forces you to consciously acknowledge that
you’re breaking Dream’s abstraction.
When You Might Need This
You might legitimately need this if:
- You need to configure Mist-specific features not exposed by Dream
- You’re integrating with libraries that expect raw Mist types
- You’re debugging server-level issues
Risks You’re Accepting
By passing risks_understood: True, you acknowledge:
- Vendor lock-in: Your code becomes coupled to Mist
- Breaking changes: If Dream switches servers or upgrades Mist, your code breaks
- Lost abstractions: You bypass Dream’s carefully designed API
Better Alternatives
Before using this, check if Dream provides:
server.bind()for network interface configurationserver.max_body_size()for request size limits- Or open a GitHub issue requesting the feature you need
Example
// You must explicitly acknowledge the risks
let mist_server = dream.get_server(app, risks_understood: True)
// Now you have raw Mist types - your code is coupled to Mist
If you must use this, isolate it in a single module and document why.
Panics
Panics if risks_understood is False. You must pass True to proceed.
pub fn get_services(
dream: Dream(server, context, services),
) -> option.Option(services)
Get the services configured for this Dream instance
Returns the services if they have been set, or None if not yet configured.
Example
let app = server.new() |> server.services(my_services)
case dream.get_services(app) {
Some(services) -> // Services are configured
None -> // No services yet
}
pub fn parse_cookie_string(
cookie_string: String,
) -> List(cookie.Cookie)
Parse a cookie header string into a list of cookies
Parses the raw cookie header value format (“name1=value1; name2=value2”) into a list of Cookie objects with simple cookies (no attributes).
Parameters
cookie_string: Raw Cookie header value
Returns
List of parsed simple cookies
Example
let cookie_str = "session=abc123; theme=dark; lang=en"
let cookies = parse_cookie_string(cookie_str)
// Returns: [
// simple_cookie("session", "abc123"),
// simple_cookie("theme", "dark"),
// simple_cookie("lang", "en")
// ]
pub fn parse_cookies_from_headers(
headers: List(header.Header),
) -> List(cookie.Cookie)
Parse cookies from HTTP headers
Extracts cookies from the Cookie header in a list of headers. Parses the cookie string format (“name1=value1; name2=value2”) into a list of Cookie objects.
Parameters
headers: List of HTTP headers to search
Returns
List of parsed cookies (empty list if no Cookie header found)
Example
import dream/http/header.{Header}
let headers = [
Header("Content-Type", "application/json"),
Header("Cookie", "session=abc123; theme=dark"),
]
let cookies = parse_cookies_from_headers(headers)
// Returns: [
// Cookie(name: "session", value: "abc123", ...),
// Cookie(name: "theme", value: "dark", ...)
// ]
pub fn route_request(
router_instance: router.Router(context, services),
request: request.Request,
context: context,
services: services,
) -> response.Response
Route a request through the router
Takes an incoming HTTP request, matches it against the router’s routes, executes any middleware, calls the appropriate controller, and returns the response.
This is the core routing function that:
- Finds a matching route based on method and path
- Extracts path parameters (e.g.,
:idfrom/users/:id) - Builds the middleware chain
- Executes middleware and controller
- Returns the response
If no route matches, returns a 404 response.
Parameters
router_instance: Router with all routes configuredrequest: HTTP request to routecontext: Request-specific contextservices: Application services (database, cache, etc.)
Returns
HTTP response from the controller or 404 if no route matches
Example
// Internal use - normally called by the request handler
let response = route_request(
my_router,
incoming_request,
request_context,
my_services
)