Kura - Ecto-inspired database layer for Erlang

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

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.

6 Likes