Hi all,
I’d like to share Kura, a database layer for Erlang that brings Ecto-style abstractions to pure Erlang, targeting PostgreSQL via pgo.
Why I built it
Working with PostgreSQL in Erlang has always meant either raw SQL, thin wrappers, or reaching for Elixir. I wanted the ergonomics that Ecto provides — schemas, changesets, composable
queries, migrations — but in idiomatic Erlang using OTP behaviours and records.
What it does
- Schemas — behaviour-based definitions with type metadata
- Changesets — cast external params, validate, track changes and errors
- Query builder — composable, functional query construction with parameterized SQL (no string interpolation)
- Associations — belongs_to, has_one, has_many, many_to_many with preloading
- Embedded schemas — embeds_one/embeds_many stored as JSONB
- Multi — atomic transaction pipelines with named steps
- Migrations — DDL operations with automatic module-based discovery
- Enums — atom-backed enum types stored as VARCHAR
- Upserts — on_conflict support (nothing, replace_all, replace specific fields)
- Telemetry — query logging with timing
Quick taste
%% Define a schema
-module(user).
-behaviour(kura_schema).
-include_lib(“kura/include/kura.hrl”).
-export([table/0, fields/0, primary_key/0]).
table() → <<“users”>>.
primary_key() → id.
fields() →
[#kura_field{name = id, type = id, primary_key = true},
#kura_field{name = name, type = string, nullable = false},
#kura_field{name = email, type = string, nullable = false},
#kura_field{name = age, type = integer}].
%% Cast, validate, insert
CS = kura_changeset:cast(user, #{}, Params, [name, email, age]),
CS1 = kura_changeset:validate_required(CS, [name, email]),
CS2 = kura_changeset:validate_format(CS1, email, <<“@”>>),
{ok, User} = my_repo:insert(CS2).
%% Composable queries
Q = kura_query:from(user),
Q1 = kura_query:where(Q, {age, ‘>’, 18}),
Q2 = kura_query:order_by(Q1, [{name, asc}]),
Q3 = kura_query:limit(Q2, 10),
{ok, Users} = my_repo:all(Q3).
Example project
pet_store is a sample REST API built with Nova and Kura, demonstrating schemas, changesets, queries, migrations, and associations in a working application.
Rebar3 plugin
rebar3_kura auto-generates migration files from schema changes — add a field to your schema, run rebar3 compile, and the migration is created for you.
Links
- Hex: kura
- HexDocs: kura
- Example app: GitHub - Taure/pet_store: Pet Store API — Nova + Kura demo
- GitHub:
Requires OTP 27+ and PostgreSQL 14+. Currently at v1.0.1.
I’d love to hear feedback, questions, or feature ideas. Happy to discuss the design choices.