Current section
Files
Jump to
Current section
Files
README.md
# Capri
Type-safe atomic Gleam bindings for Khepri.
## Getting Started
Initialize Khepri and create a `StoreHandle` with `lifecycle.bind/2`. You will use this handle for accessing and manipulating your Khepri store/cluster.
```gleam
import capri/lifecycle
import gleam/erlang/atom
let assert Ok(store) =
lifecycle.bind(atom.create("my_app"), "./data")
```
Then follow the rest of this README to get an overview of how to utilize the library.
## Repository
Repositories represent the persistence layer for terms, as defined in traditional Domain-Driven Design.
> [!TIP]
> This module doesn't use the Active Record pattern, since objects don't exist in Gleam, rendering it impossible to create "active" Records.
### Usage
Use `repository.bind/3` to open or create a repository for a given store:
```gleam
let assert Ok(users) =
repository.new("users", 1, user_decoder, [])
let users_path = path.from_key(path.String("users"))
let assert Ok(prefix) = repository.bind(store, users_path, users)
let user_path = repository.child(prefix, path.String("user-123"))
let assert Ok(Nil) = repository.put(store, user_path, user)
```
Available operations can be found at the package API docs.
## Migrations
Repository migrations are atomic, reversible, and versioned transformations over values & structure of repositories.
> [!WARNING]
>
> While 'unsafe' migrations (migrations that are not reversible) can be made, you are strongly advised to avoid them.
### Usage
```gleam
let assert Ok(one_to_two) =
repository.migration(
1,
2,
fn(entries) {
// Transform the complete keyed repository dataset.
list.map(entries, v1_to_v2)
},
fn(entries) {
list.map(entries, v2_to_v1)
},
)
let assert Ok(users) =
repository.new(
"users",
2,
user_decoder,
[one_to_two], // migration chain
)
let assert Ok(Nil) =
repository.migrate_all(store, users_path, users) // run migration chain
```
## Projections
Projections are typed, derived views maintained by Khepri in local ETS tables. They are ephemeral caches, not authoritative repository state.
### Usage
```gleam
import capri/projection
let assert Ok(by_email) =
users
|> projection.new(fn(user) { user.email })
|> projection.named(atom.create("users_by_email"))
let assert Ok(Nil) =
projection.register(store, prefix, by_email)
let assert Ok(user) =
projection.get(by_email, "reese@example.com")
```
Use `projection.many` for one-to-many lookups:
```gleam
let assert Ok(by_team) =
users
|> projection.new(fn(user) { user.team_id })
|> projection.many
|> projection.named(atom.create("users_by_team"))
let assert Ok(Nil) =
projection.register(store, prefix, by_team)
let assert Ok(team) =
projection.all(by_team, team_id)
```
Projection keys retain their Gleam type. Projection names are bounded atoms and should not be created from unbounded or user-controlled input.
Use `projection.unregister` to remove a projection and `projection.rebuild` to rebuild it from the repository.
## Lifecycle
Use `lifecycle.bind/2` to initialize Khepri, and get a `StoreHandle`. To connect to a cluster, use `lifecycle.reset_and_join_cluster/2`.
> [!WARNING]
> `lifecycle.reset_and_join_cluster/2` and `lifecycle.reset_local_member/1` erase local data.
## Naming
Khepri sounds phonetically similar to capri, and capri, capris being a type of pants, is like "pants" for Khepri :P.
## Copyright
Capri: Type-safe atomic Gleam bindings for Khepri.
Copyright (C) 2026 Software Freedom Conservancy, et. al.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.