jonnifyelixir
F6 / F6.01 / request-path

F6.01 · part 1 of 3

The request lifecycle

Trace one request all the way through and Phoenix stops being a framework and becomes a pipeline you can read. A browser sends GET /courses/42; Bandit accepts the connection and hands a conn to PortalWeb.Endpoint; the endpoint runs a fixed stack of plugs; the router matches the path to a controller action through a pipeline; the action calls Portal.courses_of/1 — the one line of domain work — and a view renders the result back into the same conn. Every stage has the same shape, a connection in and a connection out, which is why the whole path composes.

Follow the five steps, see what the endpoint’s plug stack actually contains, then read the path in code. You can curl the result.

A request, five steps

The connection is threaded through five stages, each a plain function of conn. Only the controller action does domain work; the other four are the framework moving data and matching a route. Select a step to see what it does.

The request path · select a step

request GET /courses/42 Endpoint plug stack Router match + pipeline action Portal.courses_of response render + send a connection in, a connection out — the same shape at every step

step: Controller action

does: calls the Portal facade

The pipeline is composition, plug after plug. That uniformity is why you can insert authentication, logging, or parsing as one more plug without touching the others — and why the action stays small.

Inside the endpoint

“The endpoint runs a plug stack” is concrete: a short, ordered list of plugs, each handed the connection in turn, ending in the router. In development the stack also carries code reloading and a debugger; in production it serves static files and parses the body. The order matters — sessions must be fetched before a controller can read them.

The endpoint plug stack, top to bottom

conn enters ↓ plug Plug.Static serves /assets plug Plug.RequestId · Plug.Telemetry trace + measure plug Plug.Parsers decode the body plug Plug.Session cookie session plug PortalWeb.Router match → controller each plug takes the conn and returns it; the router is the last plug in the stack

There is no magic entry point — the endpoint is a plug whose last step is another plug, the router. “Phoenix routing” is what happens when the connection reaches the bottom of this list.

The path in code

Two pieces are all the lifecycle needs to reach the engine: a route that names a controller action, and the action itself. The route is one line in the router; the action calls the facade, branches on the closed error contract, and hands a view its data. Nothing here knows how courses are stored — Portal.courses_of/1 does, and the action only calls it.

# 1. the router maps a path to a controller action (F6.02 builds this out)
get "/courses/:user_id", CourseController, :index

# 2. the action — the one step in the lifecycle that calls the engine
defmodule PortalWeb.CourseController do
  use PortalWeb, :controller

  def index(conn, %{"user_id" => uid}) do
    case Portal.courses_of(uid) do      # a facade query
      {:ok, courses} ->
        render(conn, :index, courses: courses)

      {:error, %Portal.Error{message: m}} ->
        conn |> put_status(422) |> render(:error, message: m)
    end
  end
end

a path

A verb and a URL — GET /courses/42 — matched to one action.

one facade call

The action calls Portal.courses_of/1 and renders the answer — nothing more.

Next: the endpoint, supervised — the plug at the head of this path is also a process in the F5 tree.

References

Primary sources for this lesson, and where it connects in the course.