Help us improve
Share bugs, ideas, or general feedback.
From elixir
Provides Phoenix LiveView best practices: no DB queries in mount (called twice), load data in handle_params, security scopes, scoped PubSub topics, GenServer polling, async assigns, and gotchas.
npx claudepluginhub ahmedxx99/claude-code-elixir --plugin elixirHow this skill is triggered — by the user, by Claude, or both
Slash command
/elixir:phoenix-thinkingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Mental shifts for Phoenix applications. These insights challenge typical web framework patterns.
Provides Phoenix LiveView best practices: no DB queries in mount (called twice), load data in handle_params, security scopes, scoped PubSub topics, GenServer polling, async assigns, and gotchas.
References Phoenix LiveView patterns for PubSub, uploads, components, forms, assign_async, streams. Use when building LiveView features or debugging handle_event lifecycle.
Guides Phoenix Framework development including app setup, LiveView implementation, context design, channels, routers, plugs, and project structure.
Share bugs, ideas, or general feedback.
Mental shifts for Phoenix applications. These insights challenge typical web framework patterns.
NO DATABASE QUERIES IN MOUNT
mount/3 is called TWICE (HTTP request + WebSocket connection). Queries in mount = duplicate queries.
def mount(_params, _session, socket) do
# NO database queries here! Called twice.
{:ok, assign(socket, posts: [], loading: true)}
end
def handle_params(params, _uri, socket) do
# Database queries here - once per navigation
posts = Blog.list_posts(socket.assigns.scope)
{:noreply, assign(socket, posts: posts, loading: false)}
end
mount/3 = setup only (empty assigns, subscriptions, defaults) handle_params/3 = data loading (all database queries, URL-driven state)
No exceptions: Don't query "just this one small thing" in mount. Don't "optimize later". LiveView lifecycle is non-negotiable.
Scopes address OWASP #1 vulnerability: Broken Access Control. Authorization context is threaded automatically—no more forgetting to scope queries.
def list_posts(%Scope{user: user}) do
Post |> where(user_id: ^user.id) |> Repo.all()
end
def subscribe(%Scope{organization: org}) do
Phoenix.PubSub.subscribe(@pubsub, "posts:org:#{org.id}")
end
Unscoped topics = data leaks between tenants.
Bad: Every connected user makes API calls (multiplied by users). Good: Single GenServer polls, broadcasts to all via PubSub.
Use assign_async/3 for data that can load after mount:
def mount(_params, _session, socket) do
{:ok, assign_async(socket, :user, fn -> {:ok, %{user: fetch_user()}} end)}
end
terminate/2 only fires if you're trapping exits—which you shouldn't do in LiveView.
Fix: Use a separate GenServer that monitors the LiveView process via Process.monitor/1, then handle :DOWN messages to run cleanup.
Calling start_async with the same name while a task is in-flight: the later one wins, the previous task's result is ignored.
Fix: Call cancel_async/3 first if you want to abort the previous task.
The socket in handle_out intercept is a snapshot from subscription time, not current state.
Why: Socket is copied into fastlane lookup at subscription time for performance.
Fix: Use separate topics per role, or fetch current state explicitly.
When merging classes on components, precedence is determined by stylesheet order, not HTML order. If btn-primary appears later in the compiled CSS than bg-red-500, it wins regardless of HTML order.
Fix: Use variant props instead of class merging.
The :content_type in %Plug.Upload{} is user-provided. Always validate actual file contents (magic bytes) and rewrite filename/extension.
To verify webhook signatures, you need the raw body. But Plug.Parsers consumes it.
{:ok, body, conn} = Plug.Conn.read_body(conn)
verify_signature!(conn, body)
%{conn | body_params: JSON.decode!(body)}
Don't use preserve_req_body: true—it keeps the entire body in memory for ALL requests.
%Plug.Upload{}.content_type for securityAny of these? Re-read The Iron Law and the Gotchas section.