Packages
spark
2.7.1
2.7.2
2.7.1
2.7.0
2.6.1
2.6.0
2.5.0
2.4.1
2.4.0
2.3.14
2.3.13
2.3.12
2.3.11
2.3.10
2.3.9
2.3.8
2.3.7
2.3.6
2.3.5
2.3.4
2.3.3
2.3.2
2.3.1
2.3.0
2.2.69
2.2.68
2.2.67
2.2.66
2.2.65
2.2.64
2.2.63
2.2.62
2.2.61
2.2.60
2.2.59
2.2.58
2.2.57
2.2.56
2.2.55
2.2.54
2.2.53
2.2.52
2.2.51
2.2.50
2.2.49
2.2.48
2.2.47
2.2.46
2.2.45
2.2.44
2.2.43
2.2.42
2.2.41
2.2.40
2.2.39
2.2.38
2.2.37
2.2.36
2.2.35
2.2.34
2.2.33
2.2.32
2.2.31
2.2.30
2.2.29
2.2.28
2.2.27
2.2.26
2.2.25
2.2.24
2.2.23
2.2.22
2.2.21
2.2.20
2.2.19
2.2.18
2.2.17
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.24
2.1.23
2.1.22
2.1.21
2.1.20
2.1.19
2.1.18
2.1.17
2.1.16
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.1
2.0.0
1.1.55
1.1.54
1.1.53
1.1.52
1.1.51
1.1.50
1.1.49
1.1.48
1.1.47
1.1.46
1.1.45
1.1.44
1.1.43
1.1.42
1.1.41
1.1.40
1.1.39
1.1.38
1.1.37
1.1.36
1.1.35
1.1.34
1.1.32
1.1.31
1.1.30
1.1.29
1.1.28
1.1.27
1.1.26
1.1.25
1.1.24
1.1.22
1.1.21
1.1.20
1.1.19
1.1.18
1.1.17
1.1.16
1.1.15
1.1.14
retired
1.1.13
1.1.12
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.9
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.16
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.29
0.1.28
0.1.27
0.1.26
0.1.25
0.1.24
0.1.23
0.1.22
0.1.21
0.1.20
0.1.19
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Generic tooling for building DSLs
Current section
Files
Jump to
Current section
Files
documentation/how_to/build-extensions-with-builders.md
<!--
SPDX-FileCopyrightText: 2025 spark contributors <https://github.com/ash-project/spark/graphs.contributors>
SPDX-License-Identifier: MIT
-->
# Building Extensions with the Builder API
This guide shows how to use the builder modules to define a DSL extension
programmatically. This is useful when you want to generate similar DSLs,
share schema fragments, or keep DSL construction in code instead of raw structs.
## Example: notifications extension
Define a small DSL with a single `notifications` section and a `notification`
entity. The schema uses `Field.new/3` with `(name, type, opts)` for types, defaults, docs, and
nested keys.
### Inline approach
For simple extensions, inline the builders directly in the `use` statement:
```elixir
defmodule MyApp.Notifications.Notification do
defstruct [:name, :type, :target, :metadata, :__identifier__, :__spark_metadata__]
end
defmodule MyApp.Notifications.Dsl do
alias Spark.Builder.{Entity, Field, Section}
use Spark.Dsl.Extension,
sections: [
Section.new(:notifications,
describe: "Notification configuration",
entities: [
Entity.new(:notification, MyApp.Notifications.Notification,
describe: "Defines a notification delivery",
args: [:name, :type],
schema: [
Field.new(:name, :atom, required: true, doc: "Notification name"),
Field.new(:type, {:one_of, [:email, :slack]},
required: true,
doc: "Notification type"
),
Field.new(:target, :string, doc: "Delivery target"),
Field.new(:metadata, :keyword_list,
keys: [
priority: [type: :integer, default: 0, doc: "Priority level"]
],
doc: "Optional metadata"
)
],
identifier: :name
)
|> Entity.build!()
]
)
|> Section.build!()
]
use Spark.Dsl, default_extensions: [extensions: __MODULE__]
end
```
### Helper module approach
In more complex cases, consider extracting builders into a separate module. This keeps
the DSL module clean and makes builders reusable:
```elixir
defmodule MyApp.Notifications.Notification do
defstruct [:name, :type, :target, :metadata, :__identifier__, :__spark_metadata__]
end
defmodule MyApp.Notifications.Dsl.Builder do
alias Spark.Builder.{Entity, Field, Section}
def notification_entity do
Entity.new(:notification, MyApp.Notifications.Notification,
describe: "Defines a notification delivery",
args: [:name, :type],
schema: [
Field.new(:name, :atom, required: true, doc: "Notification name"),
Field.new(:type, {:one_of, [:email, :slack]},
required: true,
doc: "Notification type"
),
Field.new(:target, :string, doc: "Delivery target"),
Field.new(:metadata, :keyword_list,
keys: [
priority: [type: :integer, default: 0, doc: "Priority level"]
],
doc: "Optional metadata"
)
],
identifier: :name
)
|> Entity.build!()
end
def notifications_section do
Section.new(:notifications,
describe: "Notification configuration",
entities: [notification_entity()]
)
|> Section.build!()
end
end
defmodule MyApp.Notifications.Dsl do
alias MyApp.Notifications.Dsl.Builder
use Spark.Dsl.Extension, sections: [Builder.notifications_section()]
use Spark.Dsl, default_extensions: [extensions: __MODULE__]
end
```
## Using the DSL
```elixir
defmodule MyApp.Config do
use MyApp.Notifications.Dsl
notifications do
notification :ops, :email do
target "ops@example.com"
metadata priority: 1
end
end
end
```