Current section
Files
Jump to
Current section
Files
doc/readme.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="ExDoc v0.40.1">
<meta name="project" content="ash_gleam v0.3.1">
<title>README — ash_gleam v0.3.1</title>
<link rel="stylesheet" href="dist/html-elixir-YJO4MOOW.css" />
<script defer src="dist/sidebar_items-4B5DC8F3.js"></script>
<script defer src="docs_config.js"></script>
<script defer src="dist/html-YU4BZFVS.js"></script>
</head>
<body>
<script>(()=>{var t="ex_doc:settings",e="dark";var o="dark",s="light";var E="sidebar_state",n="closed";var r="sidebar_width";var a="sidebar-open";var i=new URLSearchParams(window.location.search),S=i.get("theme")||JSON.parse(localStorage.getItem(t)||"{}").theme;(S===o||S!==s&&window.matchMedia("(prefers-color-scheme: dark)").matches)&&document.body.classList.add(e);var d=sessionStorage.getItem(E),A=d!==n&&!window.matchMedia(`screen and (max-width: ${768}px)`).matches;document.body.classList.toggle(a,A);var c=sessionStorage.getItem(r);c&&document.body.style.setProperty("--sidebarWidth",`${c}px`);var p=/(Macintosh|iPhone|iPad|iPod)/.test(window.navigator.userAgent);document.documentElement.classList.toggle("apple-os",p);})();
</script>
<div class="body-wrapper">
<button id="sidebar-menu" class="sidebar-button sidebar-toggle" aria-label="toggle sidebar" aria-controls="sidebar">
<i class="ri-menu-line ri-lg" title="Collapse/expand sidebar"></i>
</button>
<nav id="sidebar" class="sidebar">
<div class="sidebar-header">
<div class="sidebar-projectInfo">
<div>
<a href="api-reference.html" class="sidebar-projectName" translate="no">
ash_gleam
</a>
<div class="sidebar-projectVersion" translate="no">
v0.3.1
</div>
</div>
</div>
<ul id="sidebar-list-nav" class="sidebar-list-nav" role="tablist" data-extras="Pages"></ul>
</div>
</nav>
<output role="status" id="toast"></output>
<main class="content page-extra" id="main" data-type="extras">
<div id="content" class="content-inner">
<div class="top-search">
<div class="search-settings">
<form class="search-bar" action="search.html">
<label class="search-label">
<span class="sr-only">Search documentation of ash_gleam</span>
<div class="search-input-wrapper">
<input name="q" type="text" class="search-input" placeholder="Press / to search" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
<button type="button" tabindex="-1" class="search-close-button" aria-hidden="true">
<i class="ri-close-line ri-lg" title="Cancel search"></i>
</button>
</div>
</label>
</form>
<div class="autocomplete">
</div>
<div class="engine-selector" data-multiple="false">
<button type="button" class="engine-button" aria-label="Select search engine" aria-haspopup="true" aria-expanded="false">
<i class="ri-search-2-line" aria-hidden="true"></i>
<span class="engine-name">Default</span>
<i class="ri-arrow-down-s-line" aria-hidden="true"></i>
</button>
<div class="engine-dropdown" hidden role="menu">
<button type="button"
class="engine-option"
data-engine-url="search.html?q="
role="menuitemradio"
aria-checked="true">
<span class="name">Default</span>
<span class="help">In-browser search</span>
</button>
</div>
</div>
<button class="icon-settings display-settings">
<i class="ri-settings-3-line"></i>
<span class="sr-only">Settings</span>
</button>
</div>
</div>
<div id="top-content">
<div class="heading-with-actions top-heading">
<h1>README</h1>
<a href="readme.md" title="Copy Markdown (hold Ctrl/Cmd and click it to open as Markdown)" class="copy-markdown icon-action" rel="help">
<i class="ri-markdown-line" aria-hidden="true"></i>
<span class="sr-only">Copy Markdown</span>
</a>
</div>
<!--SPDX-FileCopyrightText: 2026 Nduati KuriaSPDX-License-Identifier: MIT--><img src="https://github.com/NduatiK/ash_gleam/blob/main/logo.png?raw=true" alt="Logo" width="300"/><p><a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"/></a>
<a href="https://hex.pm/packages/ash_gleam"><img src="https://img.shields.io/hexpm/v/ash_gleam.svg" alt="Hex version badge"/></a>
<a href="https://hexdocs.pm/ash_gleam"><img src="https://img.shields.io/badge/docs-hexdocs-purple" alt="Hexdocs badge"/></a></p><h1>AshGleam</h1><p><strong>Type-safe Gleam interop for Ash resources</strong></p><p>AshGleam integrates Elixir's Ash framework and Gleam into a single, cohesive system. It enables you to move data and execution across the boundary with compile-time guarantees.</p><p>You can:</p><ul><li><a href="#generate-gleam-types-from-your-ash-resources">Generate Gleam types from your Ash resources</a></li><li><a href="#generate-gleam-types-from-your-ash-resources">Expose Gleam functions as Ash actions</a></li><li><a href="#expose-ash-actions-to-gleam">Expose Ash actions in Gleam</a></li><li><a href="#represent-gleam-custom-types-in-elixir">Represent Gleam custom-types in Elixir</a></li></ul><h2 id="installation" class="section-heading"><a href="#installation" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Installation</span></h2><details><summary>
### With Igniter (recommended)
```bash
mix igniter.install ash_gleam
```
</summary>
```bash
mix igniter.install ash_gleam
# If testing locally:
mix igniter.install ash_gleam@path:..
```
This automatically configures your `mix.exs` with all the settings required by
[MixGleam](https://github.com/gleam-lang/mix_gleam): compilers, `erlc_paths`,
`erlc_include_path`, `prune_code_paths`, the `deps.get` alias, and the
`gleam_stdlib` / `gleeunit` dependencies. It also creates the `src/` directory
and adds `build/` to your `.gitignore`.
You will still need to install the Gleam compiler and the MixGleam archive:
```bash
# Install the Gleam compiler — see https://gleam.run/getting-started/installing-gleam.html
# Install the MixGleam Mix archive
mix archive.install hex mix_gleam
```
</details><details><summary>
### Manual setup
</summary>Add `ash_gleam` to your dependencies:
```elixir
# mix.exs
defp deps do
[
{:ash_gleam, "~> 0.1"},
{:gleam_stdlib, "~> 0.34 or ~> 1.0"},
{:gleeunit, "~> 1.0", only: [:dev, :test], runtime: false}
]
end
```
Then follow the [MixGleam README](https://github.com/gleam-lang/mix_gleam) to
configure your project:
```elixir
# mix.exs
@app :my_app
def project do
[
app: @app,
# ...
archives: [mix_gleam: "~> 0.6"],
compilers: [:gleam | Mix.compilers()],
aliases: [
"deps.get": ["deps.get", "gleam.deps.get"]
],
erlc_paths: [
"_build/dev/lib/#{@app}/_gleam_artefacts",
"_build/dev/lib/#{@app}/build"
],
erlc_include_path: "_build/dev/lib/#{@app}/include",
prune_code_paths: false
]
end
```
Create a `src/` directory for your Gleam source files and add the following to your `.gitignore`:
```
# gleam build files
/build/
# intermediate generation file
/src/generated/manifest.term
```
</details><h2 id="generate-gleam-types-from-your-ash-resources" class="section-heading"><a href="#generate-gleam-types-from-your-ash-resources" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Generate Gleam types from your Ash resources</span></h2><details> <summary>
1. Add <code>AshGleam.Resource</code> to your resource and declare a <code>gleam</code> block:
</summary>
```elixir
defmodule MyApp.Todo do
use Ash.Resource,
...,
extensions: [AshGleam.Resource, AshGleam.Actions]
gleam do
type_name "Todo" # required — the Gleam type name
module_name "todo_item" # optional — overrides the generated file name
end
attributes do
uuid_primary_key :id
attribute :title, :string, allow_nil?: false, public?: true
attribute :completed, :boolean, default: false, public?: true
end
end
```</details><details> <summary>
2. Run `mix ash_gleam.codegen`
</summary>
```gleam
// generated at src/generated/src/todo_item
pub type TicTacToe {
TicTacToe(
id: String,
title: String,
completed: Boolean,
)
}
```</details><p>Only <code class="inline">public?: true</code> attributes are included in the generated Gleam type.</p><h2 id="expose-gleam-functions-as-ash-actions" class="section-heading"><a href="#expose-gleam-functions-as-ash-actions" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Expose Gleam functions as Ash actions</span></h2><details> <summary>
1. Create a gleam function (make sure you have run the generator first)
</summary>
```gleam
// Import the generated Todo type
import src/generated/src/todo_item.{type Todo, Todo}
pub fn mark_completed(item: Todo) -> Todo {
Todo(..item, completed: True)
}
```</details><details> <summary>
2. Add <code>AshGleam.Actions</code> to your resource and declare a <code>gleam.actions</code> block for that function
</summary>
```elixir
defmodule MyApp.Todo do
use Ash.Resource,
...,
extensions: [AshGleam.Resource, AshGleam.Actions]
gleam do
type_name "Todo"
module_name "todo_item"
actions do
action :mark_completed, __MODULE__ do
update? true
argument :todo, __MODULE__, allow_nil?: false
run &:test_gleam.mark_completed/1
end
end
end
end
```</details><details> <summary>
3. Use the exposed function
</summary>
```elixir
todo = # create a todo
# mark_completed in memory
assert {:ok, updated} = MyApp.Todo.mark_completed(%{todo: todo})
# mark_completed and persist
{:ok, changeset} =
todo
|> AshGleam.Changeset.for_update(:mark_completed, %{}, action: :update)
|> Ash.update!()
```</details><details> <summary>
4. If you want a code interface that does the update for you, update your domain
</summary>
```elixir
defmodule MyApp.Domain do
use Ash.Domain,
otp_app: :my_app,
extensions: [AshGleam.Domain]
gleam do
code_interface do
resource AshGleam.TestTodo do
define_gleam_update :mark_completed, action: :update
end
end
end
end
# mark_completed and persist
{:ok, updated} = MyApp.Domain.mark_completed(todo)
```</details><h2 id="expose-ash-actions-to-gleam" class="section-heading"><a href="#expose-ash-actions-to-gleam" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Expose Ash actions to Gleam</span></h2><details> <summary>
1. Add the resource actions you want to expose to Gleam
</summary>
```elixir
defmodule MyApp.Todo do
use Ash.Resource,
...,
extensions: [AshGleam.Resource, AshGleam.Actions]
...
actions do
defaults [:read]
create :create do
accept [:title, :completed, :priority]
end
update :update do
accept [:title, :completed, :priority]
require_atomic? false
end
destroy :destroy
read :get do
get_by [:id]
end
read :first_completed do
get? true
filter expr(completed == true)
prepare build(sort: [title: :asc], limit: 1)
end
end
end
```
</details><details> <summary>
2. Create an entry in gleam.ffi for your resource actions
</summary>
```elixir
defmodule MyApp.Domain do
use Ash.Domain,
otp_app: :my_app,
extensions: [AshGleam.Domain]
gleam do
ffi do
resource MyApp.Todo do
action :list_todos, :read
action :create_todo, :create
action :get_todo, :get
action :destroy_todo, :destroy
action :first_completed, :first_completed
end
end
end
end
```
</details><ol start="3"><li>Run <code class="inline">mix ash_gleam.codegen</code></li></ol><details> <summary>
4. Use the generated gleam functions
</summary>
```gleam
import myapp/generated/src/list_todos
import myapp/generated/src/todo_item.{type TodoFilter, type TodoSort}
pub fn fetch_incomplete_todo_titles(): Result(List(String), String) {
list_todos.new()
|> list_todos.filter([todo_item.CompletedEq(False)])
|> list_todos.sort([todo_item.Title(Asc)])
|> list_todos.limit(option.Some(10))
|> list_todos.run()
|> result.map(fn (todo_item) {
todo_item.title
})
}
```
</details><h2 id="represent-gleam-custom-types-in-elixir" class="section-heading"><a href="#represent-gleam-custom-types-in-elixir" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Represent Gleam custom-types in Elixir</span></h2><p>You can define the equivalent to Gleam's custom types using <a href="https://hexdocs.pm/ash_sum_type/1.0.3/AshSumType.html"><code class="inline">AshSumType</code></a> from <a href="https://github.com/NduatiK/ash_sum_type">ash_sum_type</a>.</p><table> <thead>
<tr>
<th>Elixir</th>
<th>Generated Gleam</th>
</tr>
</thead>
<tbody>
<tr style="background-color: var(--bgColor-muted,var(--color-canvas-subtle));"><td>
```elixir
defmodule MyApp.Mark do
use AshSumType, variants: [:x, :o]
end
```
</td><td>
```gleam
pub type Mark {
X
O
}
```
</td></tr><tr style="background-color: var(--bgColor-muted,var(--color-canvas-subtle));"><td>
```elixir
defmodule MyApp.Mark do
use AshSumType
variant :x
variant :o
end
```
</td><td>
```gleam
pub type Mark {
X
O
}
```
</td></tr><tr style="background-color: var(--bgColor-muted,var(--color-canvas-subtle));"><td>
```elixir
defmodule MyApp.LookupOutcome do
use AshSumType
variant :found do
field :value, MyApp.Todo, allow_nil?: false
end
variant :missing do
field :error, :string, allow_nil?: false
end
end
```
</td><td>
```gleam
pub type LookupOutcome {
Found(Todo)
Missing(String)
}
```
</td></tr> </tbody>
</table><h2 id="embedded-resources" class="section-heading"><a href="#embedded-resources" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Embedded resources</span></h2><p>Resources with the <code class="inline">:embedded</code> data layer work as field types in other resources. The embedded resource gets its own Gleam type and is imported automatically in the parent resource's generated file.</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">MyApp.Tag</span><span class="w"> </span><span class="k" data-group-id="4727377688-1">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Ash.Resource</span><span class="p">,</span><span class="w">
</span><span class="ss">domain</span><span class="p">:</span><span class="w"> </span><span class="nc">MyApp.Domain</span><span class="p">,</span><span class="w">
</span><span class="ss">data_layer</span><span class="p">:</span><span class="w"> </span><span class="ss">:embedded</span><span class="p">,</span><span class="w">
</span><span class="ss">extensions</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="4727377688-2">[</span><span class="nc">AshGleam.Resource</span><span class="p" data-group-id="4727377688-2">]</span><span class="w">
</span><span class="n">gleam</span><span class="w"> </span><span class="k" data-group-id="4727377688-3">do</span><span class="w">
</span><span class="n">type_name</span><span class="w"> </span><span class="s">"Tag"</span><span class="w">
</span><span class="k" data-group-id="4727377688-3">end</span><span class="w">
</span><span class="n">attributes</span><span class="w"> </span><span class="k" data-group-id="4727377688-4">do</span><span class="w">
</span><span class="n">attribute</span><span class="w"> </span><span class="ss">:label</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="p">,</span><span class="w"> </span><span class="ss">allow_nil?</span><span class="p">:</span><span class="w"> </span><span class="no">false</span><span class="p">,</span><span class="w"> </span><span class="ss">public?</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="n">attribute</span><span class="w"> </span><span class="ss">:color</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="p">,</span><span class="w"> </span><span class="ss">allow_nil?</span><span class="p">:</span><span class="w"> </span><span class="no">false</span><span class="p">,</span><span class="w"> </span><span class="ss">public?</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="k" data-group-id="4727377688-4">end</span><span class="w">
</span><span class="k" data-group-id="4727377688-1">end</span><span class="w">
</span><span class="kd">defmodule</span><span class="w"> </span><span class="nc">MyApp.Todo</span><span class="w"> </span><span class="k" data-group-id="4727377688-5">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Ash.Resource</span><span class="p">,</span><span class="w">
</span><span class="n">...</span><span class="p">,</span><span class="w">
</span><span class="ss">extensions</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="4727377688-6">[</span><span class="nc">AshGleam.Resource</span><span class="p" data-group-id="4727377688-6">]</span><span class="w">
</span><span class="n">attributes</span><span class="w"> </span><span class="k" data-group-id="4727377688-7">do</span><span class="w">
</span><span class="n">...</span><span class="w">
</span><span class="c1"># Gleam type List(Tag)</span><span class="w">
</span><span class="n">attribute</span><span class="w"> </span><span class="ss">:tags</span><span class="p">,</span><span class="w"> </span><span class="p" data-group-id="4727377688-8">{</span><span class="ss">:array</span><span class="p">,</span><span class="w"> </span><span class="nc">MyApp.Tag</span><span class="p" data-group-id="4727377688-8">}</span><span class="p">,</span><span class="w"> </span><span class="ss">allow_nil?</span><span class="p">:</span><span class="w"> </span><span class="no">false</span><span class="p">,</span><span class="w"> </span><span class="ss">default</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="4727377688-9">[</span><span class="p" data-group-id="4727377688-9">]</span><span class="p">,</span><span class="w"> </span><span class="ss">public?</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="c1"># Gleam type List(Option(Tag))</span><span class="w">
</span><span class="n">attribute</span><span class="w"> </span><span class="ss">:nullable_tags</span><span class="p">,</span><span class="w"> </span><span class="p" data-group-id="4727377688-10">{</span><span class="ss">:array</span><span class="p">,</span><span class="w"> </span><span class="nc">MyApp.Tag</span><span class="p" data-group-id="4727377688-10">}</span><span class="p">,</span><span class="w"> </span><span class="ss">allow_nil?</span><span class="p">:</span><span class="w"> </span><span class="no">false</span><span class="p">,</span><span class="w"> </span><span class="ss">default</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="4727377688-11">[</span><span class="p" data-group-id="4727377688-11">]</span><span class="p">,</span><span class="w"> </span><span class="ss">public?</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="p">,</span><span class="w"> </span><span class="ss">nil_items?</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="k" data-group-id="4727377688-7">end</span><span class="w">
</span><span class="k" data-group-id="4727377688-5">end</span></code></pre><h2 id="requirements" class="section-heading"><a href="#requirements" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Requirements</span></h2><ul><li>Elixir 1.15+</li><li>Ash 3.0+</li><li>Gleam (with <code class="inline">mix_gleam</code> configured)</li></ul><h2 id="contributing" class="section-heading"><a href="#contributing" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Contributing</span></h2><ol><li>Fork the repository</li><li>Create a feature branch</li><li>Add tests for any new behaviour</li><li>Run <a href="https://hexdocs.pm/mix/Mix.Tasks.Test.html"><code class="inline">mix test</code></a> and <a href="https://hexdocs.pm/mix/Mix.Tasks.Format.html"><code class="inline">mix format</code></a></li><li>Open a pull request</li></ol><h2 id="license" class="section-heading"><a href="#license" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">License</span></h2><p>MIT — see <a href="https://github.com/NduatiK/ash_gleam/blob/main/LICENSES/MIT.txt">LICENSES/MIT.txt</a>.</p><h2 id="links" class="section-heading"><a href="#links" class="hover-link"><i class="ri-link-m" aria-hidden="true"></i></a><span class="text">Links</span></h2><ul><li><strong>Hex</strong>: <a href="https://hex.pm/packages/ash_gleam">https://hex.pm/packages/ash_gleam</a></li><li><strong>Docs</strong>: <a href="https://hexdocs.pm/ash_gleam">https://hexdocs.pm/ash_gleam</a></li><li><strong>Issues</strong>: <a href="https://github.com/NduatiK/ash_gleam/issues">https://github.com/NduatiK/ash_gleam/issues</a></li></ul>
</div>
<div class="bottom-actions" id="bottom-actions">
<div class="bottom-actions-item">
</div>
<div class="bottom-actions-item">
</div>
</div>
<footer class="footer">
<p>
<span class="line">
<a href="https://hex.pm/packages/ash_gleam/0.3.1">Hex Package</a>
<a href="https://preview.hex.pm/preview/ash_gleam/0.3.1">Hex Preview</a>
(<a href="https://preview.hex.pm/preview/ash_gleam/0.3.1/show/README.md">current file</a>)
</span>
<span class="line">
<button class="a-main footer-button display-quick-switch" title="Go to package docs">
Go to package docs
</button>
<a href="llms.txt" target="_blank">
View llms.txt
</a>
<a href="ash_gleam.epub">
Download ePub version
</a>
</span>
</p>
<p class="built-using">
Built using
<a href="https://github.com/elixir-lang/ex_doc" title="ExDoc" target="_blank" rel="help noopener" translate="no">ExDoc</a> (v0.40.1) for the
<a href="https://elixir-lang.org" title="Elixir" target="_blank" translate="no">Elixir programming language</a>
</p>
</footer>
</div>
</main>
</div>
</body>
</html>