Lesson 16 / 25
Phoenix Fundamentals
Understand Phoenix's request pipeline: endpoint, router, plugs, controllers and components.
A productive, fast web framework
Phoenix is Elixir's main web framework, known for high performance, real-time features and developer productivity. Create a project with mix phx.new shop_web_app (it generates Ecto, Tailwind and esbuild setup by default) and start it with mix phx.server. A request flows through the endpoint (the entry point, handling static files, parsing and sessions), then the router, which matches paths and pipes the request through pipelines of plugs (:browser for HTML with sessions and CSRF protection, :api for JSON), then to a controller action that returns a response. Plug is the core abstraction: a function or module that takes a %Plug.Conn{} and returns a modified conn, used for authentication, logging and headers. Since Phoenix 1.7, HTML is rendered with HEEx templates and function components, and routes are checked at compile time with verified routes using the ~p sigil. Contexts are plain Elixir modules (such as Shop.Catalog) that hold the business logic, keeping controllers thin. Generators such as mix phx.gen.html, mix phx.gen.json and mix phx.gen.auth scaffold resources and authentication.
Phoenix request flow
Endpoint, router pipeline, controller and context, then a rendered response.
Router, pipeline, plug and controller
A JSON API scope with an authentication plug.
# lib/shop_web/router.ex
defmodule ShopWeb.Router do
use ShopWeb, :router
pipeline :api do
plug :accepts, ["json"]
plug ShopWeb.Plugs.RequireApiKey
end
scope "/api", ShopWeb do
pipe_through :api
resources "/products", ProductController, only: [:index, :show]
end
end
# lib/shop_web/plugs/require_api_key.ex
defmodule ShopWeb.Plugs.RequireApiKey do
import Plug.Conn
def init(opts), do: opts
def call(conn, _opts) do
case get_req_header(conn, "x-api-key") do
[key] when key != "" -> assign(conn, :api_key, key)
_ -> conn |> send_resp(401, "missing API key") |> halt()
end
end
end
# lib/shop_web/controllers/product_controller.ex
defmodule ShopWeb.ProductController do
use ShopWeb, :controller
alias Shop.Catalog
def index(conn, _params), do: json(conn, %{data: Catalog.list_products()})
def show(conn, %{"id" => id}) do
case Catalog.get_product(id) do
nil -> conn |> put_status(:not_found) |> json(%{error: "not found"})
product -> json(conn, %{data: product})
end
end
endKeep controllers thin
Controllers translate HTTP into calls to contexts and back. Business rules belong in contexts, so they can be reused from LiveViews, background jobs and tests without a web request.
Quick check: What is a plug in Phoenix?
- A database adapter
- A CSS framework
- A function or module that receives a Plug.Conn and returns a (possibly modified) conn
- A type of process
Answer
A function or module that receives a Plug.Conn and returns a (possibly modified) conn — Plugs form the composable request pipeline.