agentsclimarketplace

Phoenix

Skill ComeOnOliver/skillshub/skills/TerminalSkills/skills/phoenix

🧠 The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.From the repository description

Install
npx -y skills add ComeOnOliver/skillshub --skill phoenix

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

6.9 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

Phoenix

Phoenix leverages the Erlang VM (BEAM) for massive concurrency and fault tolerance. LiveView enables rich, real-time UIs with server-rendered HTML β€” no JavaScript framework needed.

Installation

# Install Phoenix and create project
mix archive.install hex phx_new
mix phx.new my_app --database postgres
cd my_app
mix setup   # deps.get + ecto.create + assets

Project Structure

# Phoenix project layout
lib/my_app/
β”œβ”€β”€ application.ex          # OTP supervision tree
β”œβ”€β”€ repo.ex                 # Ecto repo
β”œβ”€β”€ accounts/               # Context module
β”‚   β”œβ”€β”€ user.ex             # Ecto schema
β”‚   └── accounts.ex         # Business logic
lib/my_app_web/
β”œβ”€β”€ endpoint.ex             # HTTP entry point
β”œβ”€β”€ router.ex               # Routes
β”œβ”€β”€ controllers/            # REST controllers
β”œβ”€β”€ live/                   # LiveView modules
β”œβ”€β”€ components/             # Function components
└── templates/              # HEEx templates

Ecto Schema and Migration

# lib/my_app/articles/article.ex β€” Ecto schema
defmodule MyApp.Articles.Article do
  use Ecto.Schema
  import Ecto.Changeset

  schema "articles" do
    field :title, :string
    field :slug, :string
    field :body, :string
    field :published, :boolean, default: false
    belongs_to :author, MyApp.Accounts.User
    timestamps()
  end

  def changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :body, :published])
    |> validate_required([:title, :body])
    |> validate_length(:title, max: 200)
    |> unique_constraint(:slug)
    |> generate_slug()
  end

  defp generate_slug(changeset) do
    case get_change(changeset, :title) do
      nil -> changeset
      title -> put_change(changeset, :slug, Slug.slugify(title))
    end
  end
end
# priv/repo/migrations/20240101000000_create_articles.exs β€” migration
defmodule MyApp.Repo.Migrations.CreateArticles do
  use Ecto.Migration

  def change do
    create table(:articles) do
      add :title, :string, null: false, size: 200
      add :slug, :string, null: false
      add :body, :text, null: false
      add :published, :boolean, default: false
      add :author_id, references(:users, on_delete: :delete_all), null: false
      timestamps()
    end

    create unique_index(:articles, [:slug])
  end
end

Context Module

# lib/my_app/articles/articles.ex β€” business logic context
defmodule MyApp.Articles do
  import Ecto.Query
  alias MyApp.Repo
  alias MyApp.Articles.Article

  def list_published do
    Article
    |> where(published: true)
    |> order_by(desc: :inserted_at)
    |> preload(:author)
    |> Repo.all()
  end

  def get_article!(id), do: Repo.get!(Article, id) |> Repo.preload(:author)

  def create_article(attrs) do
    %Article{}
    |> Article.changeset(attrs)
    |> Repo.insert()
  end

  def update_article(%Article{} = article, attrs) do
    article
    |> Article.changeset(attrs)
    |> Repo.update()
  end
end

Router

# lib/my_app_web/router.ex β€” routing
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_live_flash
    plug :put_root_layout, html: {MyAppWeb.Layouts, :root}
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", MyAppWeb do
    pipe_through :browser
    live "/articles", ArticleLive.Index, :index
    live "/articles/:slug", ArticleLive.Show, :show
  end

  scope "/api", MyAppWeb.API do
    pipe_through :api
    resources "/articles", ArticleController, only: [:index, :show, :create]
  end
end

LiveView

# lib/my_app_web/live/article_live/index.ex β€” LiveView for article list
defmodule MyAppWeb.ArticleLive.Index do
  use MyAppWeb, :live_view

  alias MyApp.Articles

  @impl true
  def mount(_params, _session, socket) do
    articles = Articles.list_published()
    {:ok, assign(socket, articles: articles, search: "")}
  end

  @impl true
  def handle_event("search", %{"query" => query}, socket) do
    articles = Articles.search(query)
    {:noreply, assign(socket, articles: articles, search: query)}
  end
end
<!-- lib/my_app_web/live/article_live/index.html.heex β€” LiveView template -->
<div>
  <h1>Articles</h1>
  <form phx-change="search" phx-debounce="300">
    <input type="text" name="query" value={@search} placeholder="Search..." />
  </form>
  <div :for={article <- @articles}>
    <h2><.link navigate={~p"/articles/#{article.slug}"}><%= article.title %></.link></h2>
    <p>By <%= article.author.name %></p>
  </div>
</div>

JSON Controller

# lib/my_app_web/controllers/api/article_controller.ex β€” API controller
defmodule MyAppWeb.API.ArticleController do
  use MyAppWeb, :controller
  alias MyApp.Articles

  def index(conn, _params) do
    articles = Articles.list_published()
    json(conn, %{data: Enum.map(articles, &article_json/1)})
  end

  def create(conn, %{"article" => params}) do
    case Articles.create_article(params) do
      {:ok, article} -> conn |> put_status(:created) |> json(%{data: article_json(article)})
      {:error, changeset} -> conn |> put_status(422) |> json(%{errors: format_errors(changeset)})
    end
  end

  defp article_json(a), do: %{id: a.id, title: a.title, slug: a.slug}
end

Channels (Real-time)

# lib/my_app_web/channels/room_channel.ex β€” WebSocket channel
defmodule MyAppWeb.RoomChannel do
  use MyAppWeb, :channel

  def join("room:" <> room_id, _params, socket) do
    {:ok, assign(socket, :room_id, room_id)}
  end

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

Testing

# test/my_app/articles_test.exs β€” context test
defmodule MyApp.ArticlesTest do
  use MyApp.DataCase
  alias MyApp.Articles

  test "create_article/1 with valid data" do
    attrs = %{title: "Test", body: "Content", author_id: user_fixture().id}
    assert {:ok, article} = Articles.create_article(attrs)
    assert article.title == "Test"
  end
end

Key Commands

# Common Mix commands
mix phx.gen.live Articles Article articles title:string body:text
mix ecto.migrate
mix phx.routes          # Show all routes
mix test
mix phx.server          # Start dev server
iex -S mix phx.server   # Start with REPL

Key Patterns

  • Organize business logic in Context modules β€” controllers/LiveViews delegate to contexts
  • Use changesets for all data validation β€” they compose and return descriptive errors
  • Use preload explicitly β€” Ecto never lazy-loads to prevent N+1
  • LiveView handle_event replaces most JavaScript interactivity
  • Use PubSub (Phoenix.PubSub.broadcast) for real-time updates across LiveView processes
  • Use Ecto.Multi for transactions spanning multiple operations

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.