AshPhoenixGenApi.Domain

Copy Markdown View Source

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
end

This 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
end

Domain-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:

  1. Resource action-level (e.g., action :foo do timeout 10_000 end)
  2. Resource section-level (e.g., gen_api do timeout 5_000 end)
  3. Domain section-level (this extension, e.g., gen_api do timeout 5_000 end)
  4. 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, []}
end

This adds the following functions to the generated supporter module:

  • build_push_config/0 - Builds a PushConfig struct from the domain config
  • push_to_gateway/2 - Pushes config to a specific gateway node
  • push_on_startup/2 - Pushes config on application startup
  • verify_on_gateway/2 - Verifies config version on a gateway node
  • resolve_push_nodes/0 - Resolves push_nodes at runtime
  • push_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")
end

gen_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

NameTypeDefaultDocs
supporter_moduleatomThe 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
serviceanyThe 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
nodesany:localDefault 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_modeany:randomDefault 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
timeoutany5000Default timeout in milliseconds for all resources in this domain. Individual resources and actions can override this. Accepts a positive integer or :infinity.
response_typeatom:asyncDefault 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_infobooleantrueDefault 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_permissionanyfalseDefault 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_callbackanyDefault 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 as {:callback, {Module, :function, []}} in the FunConfig's check_permission field. Defaults to nil.
versionString.t"0.0.1"Default version string for all resources in this domain. Used for PhoenixGenApi API versioning.
retryanyDefault 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?booleantrueWhether 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_nodesanyTarget 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_startupbooleanfalseWhether 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_encoderany:structDefault 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.