⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

What Is an Ecto-Style ORM?

This group builds a miniature ORM in the style of Ecto, the database library of the Elixir world. Most readers here have never written Elixir, so this page explains what that style is and why it is worth copying, before the following chapters build it in Zig.

The idea in one sentence

Ecto splits database work into pieces that are plain data and pure functions, and funnels every actual database interaction through one explicit gateway called the Repo.

What that looks like

In Ecto, an application defines a schema, builds queries as values, and hands them to the Repo:

# Elixir (Ecto), for flavor only
defmodule User do
  schema "users" do
    field :name, :string
    field :age, :integer
  end
end

query = from u in User, where: u.age > 18
adults = Repo.all(query)

Three separable ideas are hiding in those lines:

  1. The schema is a declaration, not a class with behavior. It states the shape of a row and nothing else.
  2. A query is a value. Building it runs no SQL; it can be composed, passed around, and inspected. Only handing it to the Repo touches the database.
  3. The Repo is the only gateway. There is no user.save(); records do not know how to persist themselves. One object owns the connection, and everything goes through it.

Why this maps so well to Zig

Each of those ideas lands on a Zig feature almost exactly:

Ecto ideaZig mechanism
schema as declarationa plain struct, read by @typeInfo at compile time
query as a valuea builder struct; chaining is just functions returning it
Repo as the gatewayRepo(Adapter), a type function with one stateful field
validations on the schemaa rules declaration, discovered with @hasDecl

Zig even improves on the original in one respect: what Ecto checks when the query runs, Zig can check when the query compiles. A misspelled field in a filter is a compile error in the query builder chapter, not a runtime database error.

What the alternative looks like

The other famous ORM shape is Active Record (Rails, Django): the record is an object that saves itself, user.save() and User.find(1). Convenient at first, it couples every model to a global connection, makes tests need a database, and hides queries behind attribute access. The Ecto shape has more visible plumbing, and in exchange every piece is testable alone, which is exactly the property the Repo chapter exploits with its recording driver.

Where each idea lives in this group

The Elixir snippet above is the only unverified code in this group; it is here as a reference point, not as something to run. Every Zig snippet that follows is compiled and executed by CI.