SuperAPI AI Directory

phoenix

Coding & development

View repository

Updated 10-15 21:49

About this skill

Phoenix

Phoenix (Elixir) provides real-time scalability (millions of connections). v1.7 + LiveView allows building rich SPAs without writing JavaScript.

When to Use

  • Real-Time Interactive Web Applications: Real-time dashboards, multiplayer games, and collaboration tools with LiveView.
  • High-Concurrency Distributed Backends: Leveraging Erlang/Elixir BEAM VM for fault-tolerant microservices.
  • Scalable WebSocket & Channel Architectures: Millions of persistent connections with minimal memory per process.
  • Maintainable Functional Web Development: Clean separation with Ecto schemas, contexts, and Plugs.

Quick Start

defmodule MyAppWeb.PageController do
  use MyAppWeb, :controller

def home(conn, _params) do render(conn, :home, layout: false) end end

In router.ex: scope "/", MyAppWeb do pipe_through :browser get "/", PageController, :home end

## Core Concepts

### Phoenix LiveView: Server-Driven Interactivity

Real-time UI state management over WebSockets without JavaScript client state:

```elixir
defmodule MyAppWeb.CounterLive do
  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    {:ok, assign(socket, count: 0)}
  end

  def handle_event("increment", _params, socket) do
    {:noreply, update(socket, :count, &(&1 + 1))}
  end

  def render(assigns) do
    ~H[
    <div class="counter-box">
      <h2 class="text-xl">Count: <%= @count %></h2>
      <button phx-click="increment" class="bg-blue-500 text-white px-4 py-2 rounded">
        Increment
      </button>
    </div>
    ]
  end
end

Ecto Contexts & Composability

Decoupled business logic and transactional database access:

defmodule MyApp.Accounts do
  import Ecto.Query, warn: false
  alias MyApp.Repo
  alias MyApp.Accounts.User

  def list_active_users do
    from(u in User, where: u.is_active == true, order_by: [desc: u.inserted_at])
    |> Repo.all()
  end

  def create_user(attrs \\ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end
end

Phoenix Channels: Low-Latency Pub/Sub

Real-time broadcasting across connected clients:

defmodule MyAppWeb.RoomChannel do
  use MyAppWeb, :channel

  def join("room:lobby", _payload, socket) do
    {:ok, socket}
  end

  def handle_in("new_msg", %{"body" => body}, socket) do
    broadcast!(socket, "new_msg", %{body: body, user: socket.assigns.user_id})
    {:reply, :ok, socket}
  end
end

Common Patterns

Real-Time LiveView Counter with PubSub Broadcasts

Problem: Synchronizing live state changes across multiple concurrent browser tabs.

Solution: Use Phoenix LiveView with PubSub broadcasting:

defmodule MyAppWeb.CounterLive do
  use MyAppWeb, :live_view

  def mount(_params, _session, socket) do
    if connected?(socket), do: Phoenix.PubSub.subscribe(MyApp.PubSub, "counter")
    {:ok, assign(socket, count: 0)}
  end

  def handle_event("inc", _params, socket) do
    new_count = socket.assigns.count + 1
    Phoenix.PubSub.broadcast(MyApp.PubSub, "counter", {:count_updated, new_count})
    {:noreply, assign(socket, count: new_count)}
  end

  def handle_info({:count_updated, count}, socket) do
    {:noreply, assign(socket, count: count)}
  end
end

Best Practices

Do:

  • Target Phoenix 1.7+ with verified routes (~p"/users") and HEEx functional components.
  • Structure domain boundaries using Phoenix Contexts rather than exposing Ecto queries in controllers.
  • Use LiveView streams (stream/3) for large collections to minimize memory usage on both client and server.
  • Use Ecto Multi (Ecto.Multi) for atomic multi-table transactional workflows.

Don't:

  • Store large binary data or huge collections in LiveView socket assigns; use temporary assigns or streams.
  • Make blocking third-party HTTP calls directly in LiveView processes without wrapping in Task.
  • Bypass Ecto changesets for user input validation.

Troubleshooting

Error Cause Solution
(Ecto.NoResultsError) expected at least one result Repo.get! or Repo.one! returned nil for query. Use non-bang variant Repo.get and handle nil safely.
phx-socket closed / reconnecting in LiveView LiveView process crashed due to unhandled handle_info or handle_event. Inspect Elixir crash stack trace in iex -S mix phx.server.
CompileError: undefined function MyAppWeb... Module name typo or Phoenix view helper not included. Run mix compile and check module namespace in my_app_web.ex.

References