ecto-patterns

Ecto Patterns Reference

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "ecto-patterns" with this command: npx skills add oliver-kriska/claude-elixir-phoenix/oliver-kriska-claude-elixir-phoenix-ecto-patterns

Ecto Patterns Reference

Reference for working with Ecto schemas, queries, and migrations.

Iron Laws — Never Violate These

  • CHANGESETS ARE FOR EXTERNAL DATA — Use cast/4 for user/API input, change/2 or put_change/3 for internal trusted data

  • NEVER USE :float FOR MONEY — Always use :decimal or :integer (cents)

  • NO RAILS-STYLE POLYMORPHIC ASSOCIATIONS — They break foreign key constraints; use multiple nullable FKs or separate join tables

  • ALWAYS PIN VALUES IN QUERIES — u.name == ^user_input is safe, string interpolation causes SQL injection

  • PRELOAD COLLECTIONS, NOT INDIVIDUALS — Preloading in loops = N+1 queries

  • CONSTRAINTS BEAT VALIDATIONS FOR RACE CONDITIONS — Validations provide quick feedback, constraints provide DB-level safety

  • SEPARATE QUERIES FOR has_many , JOIN FOR belongs_to — Avoids row multiplication

  • NO IMPLICIT CROSS JOINS — from(a in A, b in B) without on: creates Cartesian product

  • DEDUP BEFORE cast_assoc WITH SHARED DATA — When multiple parents share child data, deduplicate child records BEFORE building changesets. Dedup only works within a single changeset

Quick Schema Template

defmodule MyApp.Context.Entity do use Ecto.Schema import Ecto.Changeset

@primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id

schema "entities" do field :name, :string field :status, Ecto.Enum, values: [:draft, :active, :archived] field :amount_cents, :integer # Never :float for money! belongs_to :user, MyApp.Accounts.User timestamps(type: :utc_datetime_usec) end

def changeset(entity, attrs) do entity |> cast(attrs, [:name, :status, :amount_cents]) |> validate_required([:name]) |> foreign_key_constraint(:user_id) end end

Quick Decisions

cast vs put_change vs change

Function Use When

cast/4

External data (user input, API)

put_change/3

Internal trusted data (timestamps, computed)

change/2

Internal data from existing struct

Preload Strategy

Relationship Strategy

belongs_to

JOIN (single query)

has_many

Separate queries (avoid row multiplication)

Common Anti-patterns

Wrong Right

field :amount, :float

field :amount_cents, :integer

"SELECT * WHERE name = '#{name}'"

from(u in User, where: u.name == ^name)

Repo.all(User) |> Enum.filter(& &1.active)

from(u in User, where: u.active)

Preloading in loops Repo.preload(posts, :comments)

Repo.get!(User, user_id) with user input Repo.get(User, id)

  • handle nil

References

For detailed patterns, see:

  • references/changesets.md

  • cast vs put_change, custom validations, prepare_changes

  • references/queries.md

  • Composable queries, dynamic, subqueries, preloading

  • references/migrations.md

  • Safe migrations, concurrent indexes, NOT NULL

  • references/transactions.md

  • Repo.transact, Ecto.Multi, upserts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

oban

No summary provided by upstream source.

Repository SourceNeeds Review
General

tidewave-integration

No summary provided by upstream source.

Repository SourceNeeds Review
General

phx:full

No summary provided by upstream source.

Repository SourceNeeds Review
General

phx:verify

No summary provided by upstream source.

Repository SourceNeeds Review