Database layer

lxdb

Talk to your database, typed

Ecto-inspired database layer for Lx. Describe your data with schemas and associations, build queries by composition, run them through the Repo, wrap work in transactions and evolve the schema with migrations. Adapter-based — Postgres and SQLite today.

v0.1.0 ~70% complete

Highlights

  • Schemas & field types
  • Associations (belongs_to / has_many / has_one)
  • Composable queries
  • Aggregates (count / sum / avg / min / max)
  • Transactions
  • Adapter-based (Postgres, SQLite)

Features

Schemas

Declare a table, its fields and types (integer, string, uuid, json, datetime...) with options like primary key, unique, nullable and default — all in one place.

Associations

belongs_to, has_many and has_one describe relations between schemas and generate the right foreign keys.

Composable queries

Build queries with pipe: where / or_where / order_by / limit / offset / join (inner, left, right, full, cross) / group_by / having / distinct. Inspect the SQL with to_sql.

Aggregates

count, sum, avg, min and max as select expressions — typed and composable like the rest of the query.

Repo & transactions

all / get / get_by / insert / insert_all / update / delete through a supervised Repo. Wrap multi-step work in transaction and transaction_batch with rollback.

Migrations

Versioned Migration structs with up/down lists; ensure_migrations_table, applied_migrations, migrate_up/down drive the schema forward safely.

Examples

Schema with associations

require "lxdb_schema"

def user_schema do
  lxdb_schema:new("users")
    |> lxdb_schema:field(:name, :string, %{nullable: false})
    |> lxdb_schema:field(:email, :string, %{unique: true})
    |> lxdb_schema:field(:age, :integer)
    |> lxdb_schema:has_many(:posts, :post_schema)
end

Composable query + SQL

require "lxdb_query"

q = lxdb_query:from(user_schema())
  |> lxdb_query:where({:ilike, :name, "a%"})
  |> lxdb_query:order_desc(:name)
  |> lxdb_query:limit(10)

{:ok, {sql, params}} = lxdb_query:to_sql(q)
# SELECT * FROM users WHERE name ILIKE ? ORDER BY name DESC LIMIT 10;

Repo: insert / update / delete

require "lxdb"

lxdb:insert(:lxdb_repo, user_schema(), %{name: "Ada", email: "ada@x.io", age: 36})
lxdb:update(:lxdb_repo, user_schema(), %{age: 37}, [{:==, :name, "Ada"}])
lxdb:all(:lxdb_repo, q)
lxdb:delete(:lxdb_repo, user_schema(), [{:==, :name, "Ada"}])

Transaction

lxdb:transaction(:lxdb_repo, fn(_repo) do
  lxdb:insert(:lxdb_repo, user_schema(), %{name: "A"})
  lxdb:insert(:lxdb_repo, user_schema(), %{name: "B"})
end)

Migration

require "lxdb"

up = [
  "CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);",
  "CREATE INDEX idx_users_email ON users(email);"
]
m = %lxdb:Migration{version: 20260101000000, up: up, down: ["DROP TABLE users;"]}
lxdb:migrate_up(:lxdb_repo, m)

Install

# project.yml
dependencies:
  lxdb:
    path: ../../lx_libs/lxdb
  lxdb_postgres:
    path: ../../lx_libs/lxdb/adapters/postgres
  # or lxdb_sqlite for SQLite