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
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
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.
Sources
- Overview — Phoenix documentation — the framework at a glance.
- Request life-cycle — Phoenix documentation — endpoint to view.