toolchains/elixir/data/ecto-patterns/SKILL.md
Ecto patterns for Phoenix/Elixir apps. Covers schemas, changesets, migrations, queries, Ecto.Multi, transactions, constraints, associations, pagination, tenant partitioning, performance, and testing.
npx skillsauth add bobmatnyc/claude-mpm-skills ecto-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Ecto is the data layer for Phoenix applications: schemas, changesets, queries, migrations, and transactions. Good Ecto practice keeps domain logic in contexts, enforces constraints in the database, and uses transactions for multi-step workflows.
defmodule MyApp.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :email, :string
field :hashed_password, :string
field :confirmed_at, :naive_datetime
has_many :memberships, MyApp.Orgs.Membership
timestamps()
end
def registration_changeset(user, attrs) do
user
|> cast(attrs, [:email, :password])
|> validate_required([:email, :password])
|> validate_format(:email, ~r/@/)
|> validate_length(:password, min: 12)
|> unique_constraint(:email)
|> hash_password()
end
defp hash_password(%{valid?: true} = cs),
do: put_change(cs, :hashed_password, Argon2.hash_pwd_salt(get_change(cs, :password)))
defp hash_password(cs), do: cs
end
Guidelines
unique_constraint, foreign_key_constraint).changeset/2 for updates; avoid mass assigning without casting.def change do
create table(:users) do
add :email, :citext, null: false
add :hashed_password, :string, null: false
add :confirmed_at, :naive_datetime
timestamps()
end
create unique_index(:users, [:email])
end
Safe migration tips
concurrently: true for indexes; disable in change and wrap in up/down for Postgres.mix ecto.migrate via execute/1 or in distinct scripts; ensure idempotence.import Ecto.Query
def list_users(opts \\ %{}) do
base =
from u in MyApp.Accounts.User,
preload: [:memberships],
order_by: [desc: u.inserted_at]
Repo.all(apply_pagination(base, opts))
end
defp apply_pagination(query, %{limit: limit, offset: offset}),
do: query |> limit(^limit) |> offset(^offset)
defp apply_pagination(query, _), do: query
Patterns
preload rather than calling Repo in loops; prefer Repo.preload/2 after fetching.select to avoid loading large blobs.Repo.transaction with lock: "FOR UPDATE" in queries that need row-level locks.alias Ecto.Multi
def onboard_user(attrs) do
Multi.new()
|> Multi.insert(:user, User.registration_changeset(%User{}, attrs))
|> Multi.insert(:org, fn %{user: user} ->
Org.changeset(%Org{}, %{owner_id: user.id, name: attrs["org_name"]})
end)
|> Multi.run(:welcome, fn _repo, %{user: user} ->
MyApp.Mailer.deliver_welcome(user)
{:ok, :sent}
end)
|> Repo.transaction()
end
Guidelines
Multi.run/3 for side effects that can fail; return {:ok, value} or {:error, reason}.Multi.update_all for batch updates; include where guards to prevent unbounded writes.on_replace: :delete/:nilify to control nested changes.foreign_key_constraint/3 and unique_constraint/3 in changesets to surface DB errors cleanly.has_many :memberships) instead of automatic many_to_many when you need metadata.Scrivener, Flop, Paginator).EXPLAIN ANALYZE.put_source/2 with prefix:) — good isolation, needs per-tenant migrations.tenant_id column + row filters — simpler migrations; add partial indexes per tenant when large.Repo.stream for large exports; wrap in Repo.transaction.OpentelemetryEcto exports query timings; add DB connection pool metrics.use MyApp.DataCase, async: true
test "registration changeset validates email" do
changeset = User.registration_changeset(%User{}, %{email: "bad", password: "secretsecret"})
refute changeset.valid?
assert %{email: ["has invalid format"]} = errors_on(changeset)
end
DataCase sets up sandboxed DB; keep tests async unless transactions conflict.test/support to build valid structs quickly.development
Optimize web performance using Core Web Vitals, modern patterns (View Transitions, Speculation Rules), and framework-specific techniques
development
Best practices for documenting APIs and code interfaces, eliminating redundant documentation guidance per agent.
development
Comprehensive API design patterns covering REST, GraphQL, gRPC, versioning, authentication, and modern API best practices
development
Visual verification workflow for UI changes to accelerate code review and catch ...