CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/hex-phoenix

Peace of mind from prototype to production - comprehensive web framework for Elixir

Overview
Eval results
Files

web-foundation.mddocs/

Web Foundation and Request Handling

Phoenix's web foundation provides the core infrastructure for handling HTTP requests, routing URLs to controllers, and generating responses. The system is built on a plug-based architecture that processes requests through configurable pipelines.

Endpoint Configuration and Management

The endpoint is the entry point for all web requests and defines the foundational configuration.

Phoenix.Endpoint

defmodule Phoenix.Endpoint do
  # Supervision and lifecycle
  @callback start_link(keyword) :: Supervisor.on_start()
  @callback config(atom, term) :: term
  @callback config_change(term, term) :: term

  # URL and path generation
  @callback url() :: String.t()
  @callback struct_url() :: URI.t()
  @callback path(String.t()) :: String.t()
  @callback static_url() :: String.t()
  @callback static_path(String.t()) :: String.t()
  @callback static_integrity(String.t()) :: String.t() | nil
  @callback host() :: String.t()
  @callback script_name() :: [String.t()]

  # Server information
  @callback server_info(Plug.Conn.scheme()) :: {:ok, {String.t(), pos_integer}} | :error

  # PubSub integration
  @callback subscribe(binary, keyword) :: :ok | {:error, term}
  @callback unsubscribe(binary) :: :ok | {:error, term}
  @callback broadcast(binary, binary, term) :: :ok | {:error, term}
  @callback broadcast!(binary, binary, term) :: :ok
  @callback broadcast_from(pid, binary, binary, term) :: :ok | {:error, term}
  @callback broadcast_from!(pid, binary, binary, term) :: :ok
  @callback local_broadcast(binary, binary, term) :: :ok
  @callback local_broadcast_from(pid, binary, binary, term) :: :ok
end

Usage Example

defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_app

  # Static file serving
  plug Plug.Static,
    at: "/", from: :my_app, gzip: false,
    only: ~w(assets fonts images favicon.ico robots.txt)

  # Session configuration
  plug Plug.Session,
    store: :cookie,
    key: "_my_app_key",
    signing_salt: "signing_salt"

  # Router
  plug MyAppWeb.Router
end

# Start endpoint in application supervisor
children = [
  MyAppWeb.Endpoint
]

Supervisor.start_link(children, strategy: :one_for_one)

Routing System

Phoenix's router maps URLs to controller actions through a declarative DSL with support for pipelines, scoping, and resource routing.

Phoenix.Router

defmodule Phoenix.Router do
  # HTTP verb macros
  defmacro get(path, plug, plug_opts \\ [], options \\ [])
  defmacro post(path, plug, plug_opts \\ [], options \\ [])
  defmacro put(path, plug, plug_opts \\ [], options \\ [])
  defmacro patch(path, plug, plug_opts \\ [], options \\ [])
  defmacro delete(path, plug, plug_opts \\ [], options \\ [])
  defmacro options(path, plug, plug_opts \\ [], options \\ [])
  defmacro head(path, plug, plug_opts \\ [], options \\ [])
  defmacro match(verb_or_verbs, path, plug, plug_opts \\ [], options \\ [])

  # Resource routing
  defmacro resources(path, controller, opts \\ [])
  defmacro resources(path, controller, opts, do_block)

  # Scoping and organization
  defmacro scope(options \\ [], do: context)
  defmacro scope(path, options \\ [], do: context)
  defmacro scope(path, alias, options \\ [], do: context)

  # Pipeline system
  defmacro pipeline(name, do: block)
  defmacro plug(plug, opts \\ [])
  defmacro pipe_through(pipes)

  # Forwarding
  defmacro forward(path, plug, plug_opts \\ [], router_opts \\ [])
end

Usage Examples

defmodule MyAppWeb.Router do
  use Phoenix.Router

  # Define pipelines
  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, {MyAppWeb.LayoutView, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  # Browser routes
  scope "/", MyAppWeb do
    pipe_through :browser

    get "/", PageController, :index
    resources "/users", UserController
    resources "/posts", PostController, only: [:index, :show]
  end

  # API routes
  scope "/api/v1", MyAppWeb do
    pipe_through :api

    resources "/users", UserController, except: [:new, :edit]
  end

  # Admin routes with scoping
  scope "/admin", MyAppWeb.Admin, as: :admin do
    pipe_through [:browser, :require_admin]

    resources "/users", UserController
    resources "/settings", SettingController
  end
end

Controller System

Controllers handle incoming requests, process parameters, interact with contexts, and render responses.

Phoenix.Controller

defmodule Phoenix.Controller do
  # Request introspection
  def action_name(Plug.Conn.t()) :: atom
  def controller_module(Plug.Conn.t()) :: atom
  def router_module(Plug.Conn.t()) :: atom
  def endpoint_module(Plug.Conn.t()) :: atom

  # Response rendering
  def render(Plug.Conn.t(), binary | keyword) :: Plug.Conn.t()
  def render(Plug.Conn.t(), binary, keyword) :: Plug.Conn.t()
  def render(Plug.Conn.t(), atom, binary, keyword) :: Plug.Conn.t()

  # Format-specific responses
  def json(Plug.Conn.t(), term) :: Plug.Conn.t()
  def text(Plug.Conn.t(), iodata) :: Plug.Conn.t()
  def html(Plug.Conn.t(), iodata) :: Plug.Conn.t()
  def redirect(Plug.Conn.t(), keyword) :: Plug.Conn.t()

  # View and layout management
  def put_view(Plug.Conn.t(), atom | keyword) :: Plug.Conn.t()
  def put_new_view(Plug.Conn.t(), atom | keyword) :: Plug.Conn.t()
  def view_module(Plug.Conn.t(), atom | nil) :: atom | nil
  def put_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
  def put_new_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
  def put_root_layout(Plug.Conn.t(), binary | false | {atom, binary}) :: Plug.Conn.t()
  def layout(Plug.Conn.t(), atom | nil) :: binary | false | {atom, binary} | nil
  def root_layout(Plug.Conn.t(), atom | nil) :: binary | false | {atom, binary} | nil

  # URL helpers
  def put_router_url(Plug.Conn.t(), binary) :: Plug.Conn.t()
  def put_static_url(Plug.Conn.t(), binary) :: Plug.Conn.t()
  def current_path(Plug.Conn.t()) :: binary
  def current_path(Plug.Conn.t(), map | keyword) :: binary
  def current_url(Plug.Conn.t()) :: binary

  # Content negotiation
  def accepts(Plug.Conn.t(), [binary]) :: Plug.Conn.t()
  def put_format(Plug.Conn.t(), atom) :: Plug.Conn.t()
  def get_format(Plug.Conn.t()) :: atom

  # Security
  def protect_from_forgery(Plug.Conn.t(), keyword) :: Plug.Conn.t()
  def put_secure_browser_headers(Plug.Conn.t(), map) :: Plug.Conn.t()
  def scrub_params(Plug.Conn.t(), binary) :: Plug.Conn.t()

  # Flash messages
  def fetch_flash(Plug.Conn.t(), keyword) :: Plug.Conn.t()
  def put_flash(Plug.Conn.t(), atom | binary, term) :: Plug.Conn.t()
  def get_flash(Plug.Conn.t()) :: map
  def get_flash(Plug.Conn.t(), atom | binary) :: term
  def clear_flash(Plug.Conn.t()) :: Plug.Conn.t()
  def merge_flash(Plug.Conn.t(), Enumerable.t()) :: Plug.Conn.t()

  # File downloads
  def send_download(Plug.Conn.t(), atom, keyword) :: Plug.Conn.t()

  # JSONP support
  def allow_jsonp(Plug.Conn.t(), keyword) :: Plug.Conn.t()
end

Usage Examples

defmodule MyAppWeb.UserController do
  use Phoenix.Controller, formats: [:html, :json]

  def index(conn, _params) do
    users = MyApp.Accounts.list_users()
    render(conn, "index.html", users: users)
  end

  def show(conn, %{"id" => id}) do
    case MyApp.Accounts.get_user(id) do
      nil ->
        conn
        |> put_flash(:error, "User not found")
        |> redirect(to: "/users")

      user ->
        case get_format(conn) do
          "html" -> render(conn, "show.html", user: user)
          "json" -> json(conn, %{user: user})
        end
    end
  end

  def create(conn, %{"user" => user_params}) do
    case MyApp.Accounts.create_user(user_params) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User created successfully")
        |> redirect(to: "/users/#{user.id}")

      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    user = MyApp.Accounts.get_user!(id)
    {:ok, _user} = MyApp.Accounts.delete_user(user)

    conn
    |> put_flash(:info, "User deleted successfully")
    |> redirect(to: "/users")
  end
end

Verified Routes

Phoenix provides compile-time route verification using the ~p sigil.

Phoenix.VerifiedRoutes

defmodule Phoenix.VerifiedRoutes do
  defmacro sigil_p(path, modifiers)

  def path(Plug.Conn.t() | Phoenix.Socket.t(), binary) :: binary
  def url(Plug.Conn.t() | Phoenix.Socket.t(), binary) :: binary
end

Usage Examples

# In controllers, views, templates
def show(conn, %{"id" => id}) do
  user = MyApp.Accounts.get_user!(id)
  redirect(conn, to: ~p"/users/#{user}")
end

# In templates
<%= link "View User", to: ~p"/users/#{@user}" %>
<%= link "Edit", to: ~p"/users/#{@user}/edit" %>

# With query parameters
~p"/search?#{[q: @query, page: @page]}"

# Static paths (verified at compile time)
~p"/assets/app.css"

Configuration Options

Endpoint Configuration

# config/config.exs
config :my_app, MyAppWeb.Endpoint,
  url: [host: "localhost"],
  secret_key_base: "your_secret_key_base",
  render_errors: [view: MyAppWeb.ErrorView, accepts: ~w(html json), layout: false],
  pubsub_server: MyApp.PubSub,
  live_view: [signing_salt: "your_signing_salt"],
  server: true

Router Configuration

# Enable helpers in controllers and views
use Phoenix.Router, helpers: false  # Disable route helpers

# Custom pipeline plugs
pipeline :auth do
  plug MyAppWeb.Plugs.RequireAuth
  plug MyAppWeb.Plugs.LoadCurrentUser
end

Error Handling

# Custom errors
defmodule Phoenix.Router.NoRouteError do
  @moduledoc "Exception raised when no route is found"
  defexception [:conn, :router]
end

defmodule Phoenix.MissingRequiredKeys do
  @moduledoc "Exception raised when required configuration is missing"
  defexception [:keys]
end

# In controllers
def show(conn, %{"id" => id}) do
  case MyApp.Accounts.get_user(id) do
    nil ->
      raise Phoenix.Router.NoRouteError, conn: conn, router: __MODULE__
    user ->
      render(conn, "show.html", user: user)
  end
end

Install with Tessl CLI

npx tessl i tessl/hex-phoenix

docs

code-generation.md

index.md

presence.md

real-time.md

security.md

testing.md

web-foundation.md

tile.json