dream/servers/mist/server

Your Dream app’s entry point

This module is where your web application starts. It provides a builder pattern for configuring and starting a Dream server using Mist (the BEAM’s HTTP server).

Quick Start

import dream/servers/mist/server as dream

pub fn main() {
  dream.new()
  |> dream.services(initialize_services())
  |> dream.router(create_router())
  |> dream.listen(3000)
}

The builder pattern lets you configure your server step by step. Start with new(), add your services and router, optionally set a custom context, then listen() to start.

Custom Context

By default, Dream uses AppContext with just a request_id field. For most apps, you’ll want to define your own context type to hold user auth, session data, etc:

pub type MyContext {
  MyContext(request_id: String, user: Option(User), session: Session)
}

dream.new()
|> dream.context(MyContext(request_id: "", user: None, session: empty_session()))
|> dream.services(initialize_services())
|> dream.router(create_router())
|> dream.listen(3000)

The type system ensures your controllers receive the correct context type.

Values

pub fn bind(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    context,
    services,
  ),
  interface: String,
) -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  context,
  services,
)

Set the network interface to bind to

Defaults to binding to all interfaces. Use “localhost” or “127.0.0.1” to only accept local connections, or “0.0.0.0” to accept connections from any network interface.

Example

// Only accept local connections
dream.new()
|> dream.services(my_services)
|> dream.router(my_router)
|> dream.bind("localhost")
|> dream.listen(3000)
pub fn context(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    a,
    old_services,
  ),
  new_context: context,
) -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  context,
  old_services,
)

Set a custom context type for your application

Use this to replace AppContext with your own context type that holds user authentication, session data, or any other per-request information. The type system tracks your context through middleware and controllers.

Example

pub type MyContext {
  MyContext(request_id: String, user: Option(User))
}

dream.new()
|> dream.context(MyContext(request_id: "", user: None))
|> dream.services(my_services)
|> dream.router(my_router)
|> dream.listen(3000)
pub fn listen(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    context,
    services,
  ),
  port: Int,
) -> Nil

Start the server and listen for requests

Starts the server on the specified port and blocks forever. This is what you call in your main() function. If the server fails to start, it returns Nil immediately.

This function will panic if you haven’t called router() and services() first— you can’t run a web server without defining what it does.

Example

pub fn main() {
  dream.new()
  |> dream.services(initialize_services())
  |> dream.router(create_router())
  |> dream.listen(3000)
}
pub fn listen_without_blocking(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    context,
    services,
  ),
  port: Int,
) -> Nil

Start the server without blocking

Like listen(), but returns immediately instead of blocking forever. Useful for tests where you need the server running in the background so you can make requests to it.

Example

pub fn test_server() {
  // Start server in background
  dream.new()
  |> dream.services(test_services())
  |> dream.router(test_router())
  |> dream.listen_without_blocking(8080)
  
  // Make test requests
  let response = http_client.get("http://localhost:8080/test")
  // ... assertions ...
}
pub fn max_body_size(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    context,
    services,
  ),
  size: Int,
) -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  context,
  services,
)

Set maximum request body size in bytes

Requests with bodies larger than this will be rejected. Default is effectively unlimited (max 64-bit int). Set a reasonable limit to protect against memory exhaustion.

Example

// Limit request bodies to 10MB
dream.new()
|> dream.services(my_services)
|> dream.router(my_router)
|> dream.max_body_size(10_000_000)
|> dream.listen(3000)
pub fn new() -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  context.AppContext,
  router.EmptyServices,
)

Create a new Dream server with defaults

Returns a server configured with AppContext (just a request_id field) and EmptyServices (no dependencies). You’ll typically chain this with context(), services(), and router() before calling listen().

Example

dream.new()
|> dream.services(my_services)
|> dream.router(my_router)
|> dream.listen(3000)
pub fn router(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    context,
    services,
  ),
  router_instance: router.Router(context, services),
) -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  context,
  services,
)

Provide your application’s router

The router defines which controllers handle which requests. It must be configured with the same context and services types you’ve set up, which the type system enforces.

Example

pub fn create_router() -> Router(MyContext, Services) {
  router.new
  |> router.get("/", controllers.index)
  |> router.get("/users/:id", controllers.show_user)
}

dream.new()
|> dream.services(initialize_services())
|> dream.router(create_router())
|> dream.listen(3000)
pub fn services(
  dream_instance: dream.Dream(
    mist.Builder(mist.Connection, mist.ResponseData),
    old_context,
    router.EmptyServices,
  ),
  services_instance: services,
) -> dream.Dream(
  mist.Builder(mist.Connection, mist.ResponseData),
  old_context,
  services,
)

Provide your application’s services

Services are shared dependencies available to all requests—database connections, HTTP clients, caches, etc. Define a type that holds all your services and pass it here.

Example

pub type Services {
  Services(db: Connection, cache: Cache)
}

pub fn initialize_services() -> Services {
  let db = connect_to_database()
  let cache = create_cache()
  Services(db: db, cache: cache)
}

dream.new()
|> dream.services(initialize_services())
|> dream.router(my_router)
|> dream.listen(3000)
Search Document