पाठ 18 / 25
Contexts, JSON APIs and Authentication
Organise business logic into contexts and secure APIs and pages.
Designing a Phoenix application
Phoenix encourages grouping related functionality into contexts, public modules that form the boundary of a domain area, such as Shop.Catalog, Shop.Orders and Shop.Accounts. Controllers, LiveViews and jobs call context functions like Orders.place_order(customer, cart), never the Repo or schemas directly, which keeps business rules in one tested place and makes later extraction easier. For JSON APIs, use the :api pipeline, render maps with json/2 or JSON view modules, return proper status codes (201 on creation, 422 with changeset errors) and handle failures centrally with action_fallback, a controller that turns {:error, ...} results into responses. Authentication: mix phx.gen.auth generates a secure, customisable email-based login system with hashed passwords (bcrypt, pbkdf2 or argon2), session tokens and confirmation flows; APIs commonly use bearer tokens via Phoenix.Token or libraries. Phoenix includes CSRF protection, secure headers and parameterised queries by default. Validate all input through changesets and authorise actions in contexts.
A JSON controller with action_fallback
Contexts return tuples; the fallback controller renders errors.
defmodule ShopWeb.OrderController do
use ShopWeb, :controller
alias Shop.Orders
action_fallback ShopWeb.FallbackController
def create(conn, %{"order" => order_params}) do
customer = conn.assigns.current_customer
with {:ok, order} <- Orders.place_order(customer, order_params) do
conn
|> put_status(:created)
|> json(%{data: %{id: order.id, total_paise: order.total_paise}})
end
end
end
defmodule ShopWeb.FallbackController do
use ShopWeb, :controller
def call(conn, {:error, %Ecto.Changeset{} = changeset}) do
errors =
Ecto.Changeset.traverse_errors(changeset, fn {msg, opts} ->
Enum.reduce(opts, msg, fn {key, value}, acc ->
String.replace(acc, "%{#{key}}", to_string(value))
end)
end)
conn |> put_status(422) |> json(%{errors: errors})
end
def call(conn, {:error, :not_found}) do
conn |> put_status(:not_found) |> json(%{error: "not found"})
end
def call(conn, {:error, :out_of_stock}) do
conn |> put_status(:conflict) |> json(%{error: "item out of stock"})
end
endContexts are boundaries, not folders
A context is valuable because other code goes through its public functions. If controllers start calling Repo directly, the boundary is lost and business rules spread across the web layer.
त्वरित जाँच: What does action_fallback do in a Phoenix controller?
- Sends non-conn return values such as {:error, ...} to a fallback controller that renders the response
- Retries the action
- Caches responses
- Disables CSRF
Answer
Sends non-conn return values such as {:error, ...} to a fallback controller that renders the response — It centralises error rendering for actions that return error tuples.