Ash Domain extension for PhoenixGenApi configuration.
This extension provides domain-level configuration for PhoenixGenApi, including default service settings and automatic generation of a "supporter" module that aggregates FunConfigs from all resources in the domain.
The generated supporter module implements the PhoenixGenApi client
config interface (get_config/1, get_config_version/1), allowing
gateway nodes to pull API configurations from service nodes.
Usage
Add the extension to your Ash domain:
defmodule MyApp.Chat do
use Ash.Domain,
extensions: [AshPhoenixGenApi.Domain]
gen_api do
service "chat"
nodes {ClusterHelper, :get_nodes, [:chat]}
choose_node_mode :random
timeout 5_000
response_type :async
request_info true
version "0.0.1"
supporter_module MyApp.Chat.GenApiSupporter
end
resources do
resource MyApp.Chat.DirectMessage
resource MyApp.Chat.GroupMessage
end
endThis will generate MyApp.Chat.GenApiSupporter with:
defmodule MyApp.Chat.GenApiSupporter do
@moduledoc """
Auto-generated PhoenixGenApi supporter module for MyApp.Chat.
Aggregates FunConfigs from all resources in the domain.
"""
alias PhoenixGenApi.Structs.FunConfig
def get_config(remote_id) do
{:ok, fun_configs()}
end
def get_config_version(remote_id) do
{:ok, "0.0.1"}
end
def fun_configs do
MyApp.Chat.DirectMessage.__ash_phoenix_gen_api_fun_configs__() ++
MyApp.Chat.GroupMessage.__ash_phoenix_gen_api_fun_configs__()
end
def list_request_types do
fun_configs() |> Enum.map(& &1.request_type)
end
def get_fun_config(request_type) do
fun_configs() |> Enum.find(&(&1.request_type == request_type))
end
endDomain-Level Defaults
Domain-level settings serve as defaults for all resources in the domain
that use AshPhoenixGenApi.Resource. Each resource can override these
defaults in its own gen_api section.
Resolution order for any setting:
- Resource action-level (e.g.,
action :foo do timeout 10_000 end) - Resource section-level (e.g.,
gen_api do timeout 5_000 end) - Domain section-level (this extension, e.g.,
gen_api do timeout 5_000 end) - Built-in defaults (e.g., timeout defaults to 5000)
Gateway Node Configuration
On the Phoenix gateway node, configure the supporter module in config.exs:
config :phoenix_gen_api, :gen_api,
service_configs: [
%{
service: "chat",
nodes: {ClusterHelper, :get_nodes, [:chat]},
module: MyApp.Chat.GenApiSupporter,
function: :get_config,
args: [:gateway_1]
}
]Active Push Configuration
In addition to the pull-based model (where the gateway pulls config from service nodes), you can configure the supporter module to actively push its configuration to gateway nodes.
Set push_nodes to specify which gateway nodes to push to:
gen_api do
service "chat"
supporter_module MyApp.Chat.GenApiSupporter
version "0.0.1"
push_nodes [:"gateway1@host", :"gateway2@host"]
# Or use an MFA tuple for runtime resolution:
# push_nodes {ClusterHelper, :get_gateway_nodes, []}
endThis adds the following functions to the generated supporter module:
build_push_config/0- Builds aPushConfigstruct from the domain configpush_to_gateway/2- Pushes config to a specific gateway nodepush_on_startup/2- Pushes config on application startupverify_on_gateway/2- Verifies config version on a gateway noderesolve_push_nodes/0- Resolvespush_nodesat runtimepush_to_configured_nodes/1- Pushes to all configured push_nodes
Example usage during application startup:
def start(_type, _args) do
# ... start supervision tree, then:
MyApp.Chat.GenApiSupporter.push_to_configured_nodes()
# Or push to a specific node:
MyApp.Chat.GenApiSupporter.push_on_startup(:"gateway1@host")
endgen_api
Configure PhoenixGenApi at the domain level.
Domain-level settings serve as defaults for all resources in the domain
that use AshPhoenixGenApi.Resource. Each resource can override these
defaults in its own gen_api section.
The supporter_module option defines the name of the module that will
be auto-generated to aggregate FunConfigs from all resources. This module
implements the PhoenixGenApi client config interface.
Examples
gen_api do
service "chat"
nodes {ClusterHelper, :get_nodes, [:chat]}
choose_node_mode :random
timeout 5_000
response_type :async
request_info true
version "0.0.1"
supporter_module MyApp.Chat.GenApiSupporter
end
# Minimal configuration
gen_api do
service "chat"
supporter_module MyApp.Chat.GenApiSupporter
end
Options
| Name | Type | Default | Docs |
|---|---|---|---|
supporter_module | atom | The name of the module to generate that will serve as the PhoenixGenApi supporter for this domain. This module will be auto-generated with functions: - get_config/1 - Returns {:ok, fun_configs()} for PhoenixGenApi pull - get_config_version/1 - Returns {:ok, version} for version checking - fun_configs/0 - Returns the aggregated list of FunConfig structs - list_request_types/0 - Returns all available request type strings - get_fun_config/1 - Returns a specific FunConfig by request_type Example: MyApp.Chat.GenApiSupporter | |
service | any | The service name for this domain's API endpoints. This serves as the default for all resources in the domain. Accepts a string or atom. Example: "chat", "user_service", :notification | |
nodes | any | :local | Default target nodes for all resources in this domain. Can be: - A list of node atoms: [:"node1@host", :"node2@host"] - An MFA tuple that returns a node list at runtime: {ClusterHelper, :get_nodes, [:chat]} - :local - Execute on the local node (default) |
choose_node_mode | any | :random | Default node selection strategy for all resources in this domain. - :random - Select a random node (default) - :hash - Hash-based selection using request_type - {:hash, key} - Hash-based selection using the specified argument key - :round_robin - Round-robin across nodes |
timeout | any | 5000 | Default timeout in milliseconds for all resources in this domain. Individual resources and actions can override this. Accepts a positive integer or :infinity. |
response_type | atom | :async | Default response mode for all resources in this domain. - :sync - Client waits for the result - :async - Client receives an ack, then the result later (default) - :stream - Client receives streamed chunks - :none - Fire and forget |
request_info | boolean | true | Default for whether to pass request info (user_id, device_id, request_id) as the last argument to the MFA function for all resources in this domain. |
check_permission | any | false | Default permission check mode for all resources in this domain. - false - No permission check (default) - :any_authenticated - Requires a valid user_id - {:arg, "arg_name"} - The specified argument must match user_id - {:role, ["admin"]} - User must have one of the listed roles |
permission_callback | any | Default permission callback MFA for all resources in this domain. When set, takes precedence over check_permission. Accepts {Module, :function, []} or nil. The callback function receives request_type (string) and args (map) as arguments and returns true (continue) or false (permission denied). The callback function signature: @callback checkpermission(request_type :: String.t(), args :: map()) :: boolean() Example callback: def check_permission(request_type, args) do case request_type do "delete_user" -> args["role"] == "admin" "update_profile" -> args["user_id"] == args["target_user_id"] -> true end end When both permission_callback and check_permission are set, permission_callback takes precedence and is stored in the FunConfig's permission_callback field. Defaults to nil. | |
version | String.t | "0.0.1" | Default version string for all resources in this domain. Used for PhoenixGenApi API versioning. |
retry | any | Default retry configuration for all resources in this domain. - nil - No retry (default) - A positive number n - Equivalent to {:all_nodes, n} - {:same_node, n} - Retry on the same node(s) - {:all_nodes, n} - Retry across all available nodes | |
define_supporter? | boolean | true | Whether to auto-generate the supporter module. Set to false if you want to define the supporter module manually. When false, the extension will still collect FunConfigs from resources but will not generate the supporter module. You can use AshPhoenixGenApi.Domain.Info.fun_configs/1 to get the aggregated FunConfigs and build your own supporter module. |
push_nodes | any | Target gateway nodes to push config to. Can be: - A list of node atoms: [:"gateway1@host", :"gateway2@host"] - An MFA tuple that returns a node list at runtime: {ClusterHelper, :get_gateway_nodes, []} - nil - No push nodes configured (default) When set, the generated supporter module will include functions to actively push its configuration to the specified gateway nodes. | |
push_on_startup | boolean | false | Whether to automatically push config to the configured push_nodes on application startup. When true, the supporter module's push_on_startup/2 function can be called during application startup to push the config to gateway nodes. Note: you still need to hook this into your application's supervision tree or startup sequence manually. |
result_encoder | any | :struct | Default result encoding mode for all resources in this domain. Determines how the result returned from the action MFA call is encoded before being returned to the caller. - :struct — Return the Ash resource struct as-is (default) - :map — Convert the Ash resource struct to a map containing only public fields (using Ash.Resource.Info.public_fields/1 to filter; falls back to Map.from_struct/1 for non-Ash-resource structs) - {Module, :function, args} — Custom encoder MFA. The function receives the result as its first argument, followed by args, and must return the encoded result. Individual resources and actions can override this with their own result_encoder option. For :map encoding, Ash resource structs are converted to maps containing only their public fields (attributes, calculations, aggregates, relationships). Lists of structs are mapped accordingly. Non-Ash-resource structs fall back to Map.from_struct/1. For custom MFA encoders, the function receives the result and must return the encoded value. Defaults to :struct. |