Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    bobmatnyc

    phoenix-api-channels

    bobmatnyc/phoenix-api-channels
    Coding
    10

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Phoenix controllers, JSON APIs, Channels, and Presence on the BEAM

    SKILL.md

    Phoenix APIs, Channels, and Presence (Elixir/BEAM)

    Phoenix excels at REST/JSON APIs and WebSocket Channels with minimal boilerplate, leveraging the BEAM for fault tolerance, lightweight processes, and supervised PubSub/Presence.

    Core pillars

    • Controllers for JSON APIs with plugs, pipelines, and versioning.
    • Contexts own data (Ecto schemas + queries) and expose a narrow API to controllers/channels.
    • Channels + PubSub for fan-out real-time updates; Presence for tracking users/devices.
    • Auth via plugs (session/cookie for browser, token/Bearer for APIs), with signed params.

    Project Setup

    mix phx.new my_api --no-html --no-live
    cd my_api
    mix deps.get
    mix ecto.create
    mix phx.server
    

    Key files:

    • lib/my_api_web/endpoint.ex — plugs, sockets, instrumentation
    • lib/my_api_web/router.ex — pipelines, scopes, versioning, sockets
    • lib/my_api_web/controllers/* — REST/JSON controllers
    • lib/my_api/* — contexts + Ecto schemas (ownership of data logic)
    • lib/my_api_web/channels/* — Channel modules

    Routing and Pipelines

    Separate browser vs API pipelines; version APIs with scopes.

    defmodule MyApiWeb.Router do
      use MyApiWeb, :router
    
      pipeline :api do
        plug :accepts, ["json"]
        plug :fetch_session
        plug :protect_from_forgery
        plug MyApiWeb.Plugs.RequireAuth
      end
    
      scope "/api", MyApiWeb do
        pipe_through :api
    
        scope "/v1", V1, as: :v1 do
          resources "/users", UserController, except: [:new, :edit]
          post "/sessions", SessionController, :create
        end
      end
    
      socket "/socket", MyApiWeb.UserSocket,
        websocket: [connect_info: [:peer_data, :x_headers]],
        longpoll: false
    end
    

    Tips

    • Keep pipelines short; push auth/guards into plugs.
    • Expose socket "/socket" for Channels; restrict transports as needed.

    Controllers and Plugs

    Controllers stay thin; contexts own the logic.

    defmodule MyApiWeb.V1.UserController do
      use MyApiWeb, :controller
      alias MyApi.Accounts
    
      action_fallback MyApiWeb.FallbackController
    
      def index(conn, _params) do
        users = Accounts.list_users()
        render(conn, :index, users: users)
      end
    
      def create(conn, params) do
        with {:ok, user} <- Accounts.register_user(params) do
          conn
          |> put_status(:created)
          |> put_resp_header("location", ~p\"/api/v1/users/#{user.id}\")
          |> render(:show, user: user)
        end
      end
    end
    

    FallbackController centralizes error translation ({:error, :not_found} → 404 JSON).

    Plugs

    • RequireAuth verifies bearer/session tokens, sets current_user.
    • Use plug :scrub_params-style transforms in pipelines, not controllers.
    • Avoid heavy work in plugs; they run per-request.

    Contexts and Data (Ecto)

    Contexts expose only what controllers/channels need.

    defmodule MyApi.Accounts do
      import Ecto.Query, warn: false
      alias MyApi.{Repo, Accounts.User}
    
      def list_users, do: Repo.all(User)
      def get_user!(id), do: Repo.get!(User, id)
    
      def register_user(attrs) do
        %User{}
        |> User.registration_changeset(attrs)
        |> Repo.insert()
      end
    end
    

    Guidelines

    • Keep schema modules free of controller knowledge.
    • Validate at the changeset; use Ecto.Multi for multi-step operations.
    • Prefer pagination helpers (Scrivener, Flop) for large lists.

    Channels, PubSub, and Presence

    Channel module example:

    defmodule MyApiWeb.RoomChannel do
      use Phoenix.Channel
      alias Phoenix.Presence
    
      def join("room:" <> room_id, _payload, socket) do
        send(self(), :after_join)
        {:ok, assign(socket, :room_id, room_id)}
      end
    
      def handle_info(:after_join, socket) do
        Presence.track(socket, socket.assigns.user_id, %{online_at: System.system_time(:second)})
        push(socket, "presence_state", Presence.list(socket))
        {:noreply, socket}
      end
    
      def handle_in("message:new", %{"body" => body}, socket) do
        broadcast!(socket, "message:new", %{user_id: socket.assigns.user_id, body: body})
        {:noreply, socket}
      end
    end
    

    PubSub from contexts

    def create_order(attrs) do
      with {:ok, order} <- %Order{} |> Order.changeset(attrs) |> Repo.insert() do
        Phoenix.PubSub.broadcast(MyApi.PubSub, "orders", {:order_created, order})
        {:ok, order}
      end
    end
    

    Best practices

    • Authorize in UserSocket.connect/3 before joining topics.
    • Limit payload sizes; validate incoming events.
    • Use topic partitioning for tenancy ("tenant:" <> tenant_id <> ":room:" <> room_id).

    Authentication Patterns

    • API tokens: Accept authorization: Bearer <token>; verify in plug, assign current_user.
    • Signed params: Phoenix.Token.sign/verify for short-lived join params.
    • Rate limiting: Use plugs + ETS/Cachex or reverse proxy (NGINX/Cloudflare).
    • CORS: Configure in Endpoint with cors_plug.

    Testing

    Use generated helpers:

    defmodule MyApiWeb.UserControllerTest do
      use MyApiWeb.ConnCase, async: true
    
      test "lists users", %{conn: conn} do
        conn = get(conn, ~p\"/api/v1/users\")
        assert json_response(conn, 200)["data"] == []
      end
    end
    

    Channel tests:

    defmodule MyApiWeb.RoomChannelTest do
      use MyApiWeb.ChannelCase, async: true
    
      test "broadcasts messages" do
        {:ok, _, socket} = connect(MyApiWeb.UserSocket, %{"token" => "abc"})
        {:ok, _, socket} = subscribe_and_join(socket, "room:123", %{})
        ref = push(socket, "message:new", %{"body" => "hi"})
        assert_reply ref, :ok
        assert_broadcast "message:new", %{body: "hi"}
      end
    end
    

    DataCase: isolates DB per test; use fixtures/factories for setup.


    Telemetry, Observability, and Ops

    • :telemetry events from endpoint, controller, channel, and Ecto queries; export via OpentelemetryPhoenix and OpentelemetryEcto.
    • Use Plug.Telemetry for request metrics; add logging metadata (request_id, user_id).
    • Releases: MIX_ENV=prod mix release; configure runtime in config/runtime.exs.
    • Clustering: libcluster + distributed PubSub for multi-node Presence.
    • Assetless APIs: disable unused watchers (esbuild/tailwind) for API-only apps.

    Common Pitfalls

    • Controllers doing queries directly instead of delegating to contexts.
    • Not authorizing in UserSocket.connect/3, leading to topic exposure.
    • Missing action_fallback → inconsistent error shapes.
    • Forgetting to limit event payloads; large messages can overwhelm channels.
    • Leaving longpoll enabled when unused; disable to reduce surface area.

    Phoenix API + Channels shine when contexts own data, controllers stay thin, and Channels use PubSub/Presence with strict authorization and telemetry. The BEAM handles concurrency and fault tolerance; focus on clear boundaries and real-time experiences.

    Recommended Servers
    Discord
    Discord
    Postman
    Postman
    Youtube
    Youtube
    Repository
    bobmatnyc/claude-mpm-skills
    Files