100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Ecto and Databases

How Ecto's Repo, Schema, Changeset, and Query modules work together to safely model, validate, and query data in Elixir.

Practical ElixirIntermediate10 min readJul 10, 2026
Analogies

What Is Ecto?

Ecto is Elixir's database toolkit — not exactly an ORM in the traditional sense, since it deliberately avoids hiding SQL behind object mapping magic. It's composed of four main pieces: Ecto.Repo (the interface to your database), Ecto.Schema (mapping database tables to Elixir structs), Ecto.Changeset (validating and casting data before writes), and Ecto.Query (a composable, type-checked DSL for building SQL queries).

🏏

Cricket analogy: Ecto acts as the team's official scorer, translating raw play (database rows) into a structured scorecard (Elixir structs) that the rest of the team can trust.

Schemas and Changesets

A schema defines the shape of a table using schema/2, listing fields and their types, e.g., field :title, :string. A changeset takes a struct (or schema) and a map of parameters, casts the allowed fields with cast/3, then applies validations like validate_required/2 or validate_length/3. Only a valid changeset can be successfully passed to Repo.insert/1 or Repo.update/1 — invalid ones return {:error, changeset} with structured error messages.

🏏

Cricket analogy: A changeset is like the third umpire reviewing a run-out — it checks the incoming data against the rules (validations) before confirming it's out, rejecting anything that doesn't hold up.

elixir
defmodule MyApp.Blog.Post do
  use Ecto.Schema
  import Ecto.Changeset

  schema "posts" do
    field :title, :string
    field :body, :string
    field :published, :boolean, default: false

    timestamps()
  end

  def changeset(post, attrs) do
    post
    |> cast(attrs, [:title, :body, :published])
    |> validate_required([:title, :body])
    |> validate_length(:title, min: 3, max: 200)
  end
end

Querying with Ecto.Query and Repo

Ecto.Query lets you build queries with a composable, pipeable syntax: from p in Post, where: p.published == true, order_by: [desc: p.inserted_at]. Because clauses can be added incrementally to a query variable across function calls, it's common to build dynamic queries by piping a base query through several where or join calls before finally passing it to Repo.all/1, Repo.one/1, or Repo.get/2.

🏏

Cricket analogy: Ecto.Query composing filters is like a captain setting a field placement step by step — first slips, then gully, then point — building up the final query the same incremental way.

Migrations

Migrations are versioned Elixir modules (generated with mix ecto.gen.migration) that describe structural changes to the database — creating tables, adding columns, adding indexes — using functions like create table/1 and add/3. Running mix ecto.migrate applies pending migrations in order and records which have run in a schema_migrations table, so the schema's history stays reproducible across environments.

🏏

Cricket analogy: A migration is like laying a new pitch before the season starts — a permanent, versioned change to the playing surface (schema) that every subsequent match relies on.

Never edit a migration file that has already been run in a shared environment (staging or production) — Ecto tracks migrations by version number and won't re-run an already-applied one, so your change will silently be missing from other environments. Write a new migration instead.

  • Ecto has four core pieces: Repo, Schema, Changeset, and Query.
  • Schemas map database tables to structs; fields and their types are declared with field/3.
  • Changesets cast and validate incoming data before it reaches the database via cast/3 and validate_* functions.
  • Ecto.Query is a composable DSL — queries can be built incrementally by piping where/order_by/join clauses.
  • Repo.insert/update/get/all execute queries and changesets against the configured database.
  • Migrations are versioned, one-directional records of schema changes tracked in schema_migrations.
  • Never modify an already-run migration in a shared environment — write a new one instead.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ElixirStudyNotes#EctoAndDatabases#Ecto#Databases#Schemas#Changesets#SQL#StudyNotes#SkillVeris