AshPhoenixGenApi.Resource

Copy Markdown View Source

Ash extension for generating PhoenixGenApi function configurations from Ash resources.

This extension allows you to define PhoenixGenApi endpoints directly in your Ash resource DSL, automatically generating FunConfig structs that can be pulled by gateway nodes.

Usage

Add the extension to your resource:

defmodule MyApp.Chat.DirectMessage do
  use Ash.Resource,
    extensions: [AshPhoenixGenApi.Resource]

  gen_api do
    service "chat"
    nodes {ClusterHelper, :get_nodes, [:chat]}
    choose_node_mode :random

    action :send_direct_message do
      request_type "send_direct_message"
      timeout 10_000
      response_type :async
      request_info true
    end

    action :get_conversation do
      timeout 5_000
    end

    mfa :ping do
      request_type "ping"
      mfa {MyApp.Chat.Api, :ping, []}
      arg_types %{}
    end
  end
end

DSL

See the DSL documentation for the full list of configuration options.

gen_api

Configure PhoenixGenApi at the resource level.

The gen_api section defines which Ash actions should be exposed as PhoenixGenApi endpoints, along with their routing, timeout, permission, and other settings.

Section-level options serve as defaults for all action and mfa entities in this resource. Each entity can override these defaults.

Nested DSLs

Examples

gen_api do
  service "chat"
  nodes {ClusterHelper, :get_nodes, [:chat]}
  choose_node_mode :random
  timeout 5_000
  response_type :async
  request_info true

  action :send_direct_message do
    request_type "send_direct_message"
    timeout 10_000
  end

  action :get_conversation do
    timeout 5_000
  end
end
# Minimal configuration with defaults
gen_api do
  service "chat"

  action :create
  action :read
end

Options

NameTypeDefaultDocs
serviceanyThe service name for this resource's API endpoints. This is used by PhoenixGenApi to group and route API calls. Accepts a string or atom. Example: "chat", "user_service", :notification
nodesany:localDefault target nodes for all actions in this resource. 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 actions. - :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 actions. Individual actions can override this. Accepts a positive integer or :infinity.
response_typeatom:asyncDefault response mode for all actions. - :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.
check_permissionanyfalseDefault permission check mode for all actions. - 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 actions. 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.
versionString.t"0.0.1"Default version string for all actions. Used for PhoenixGenApi API versioning.
retryanyDefault retry configuration for all actions. - 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
code_interface?booleantrueWhether to auto-generate code interface functions for the gen_api actions on the resource module. When true, a function matching each action name will be defined on the resource module that calls the action through the Ash framework. Individual actions can override this with their own code_interface? option. Defaults to true.
result_encoderany:structDefault result encoding mode for all actions. 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 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.

gen_api.action

action name

Configures a PhoenixGenApi endpoint for an Ash resource action.

Each action entity maps an Ash action to a PhoenixGenApi FunConfig. If request_type is not specified, it defaults to the action name as a string. If arg_types and arg_orders are not specified, they are auto-derived from the Ash action's accepted attributes and arguments. arg_orders defaults to :map, which derives the order from arg_types keys. Set to a list of argument name strings to specify explicit ordering.

Examples

action :send_direct_message do
  request_type "send_direct_message"
  timeout 10_000
  response_type :async
  request_info true
  check_permission {:arg, "from_user_id"}
end
action :get_conversation do
  timeout 5_000
  response_type :async
end
# Minimal config - auto-derives request_type and args from Ash action
action :create

Arguments

NameTypeDefaultDocs
nameatomThe name of the Ash resource action to expose as a PhoenixGenApi endpoint. Must match an existing action defined on the resource.

Options

NameTypeDefaultDocs
request_typeString.tThe PhoenixGenApi request type string used by clients to call this endpoint. Defaults to the action name converted to a string. Example: "send_direct_message", "get_conversation"
timeoutanyTimeout in milliseconds for the function call. Defaults to the gen_api section-level timeout (which defaults to 5000). Accepts a positive integer or :infinity.
response_typeatomThe response mode for this endpoint. - :sync - Client waits for the result - :async - Client receives an ack, then the result later - :stream - Client receives streamed chunks - :none - Fire and forget Defaults to the gen_api section-level response_type (which defaults to :async).
request_infobooleanWhether to pass request info (user_id, device_id, request_id) as the last argument to the MFA function. When true, the generated MFA will append a map with %{user_id: ..., device_id: ..., request_id: ...} to the function arguments. Defaults to the gen_api section-level request_info (which defaults to true).
check_permissionanyPermission check mode for this endpoint. - false - No permission check - :any_authenticated - Requires a valid user_id - {:arg, "arg_name"} - The specified argument must match user_id - {:role, ["admin", "moderator"]} - User must have one of the listed roles Defaults to the gen_api section-level check_permission (which defaults to false).
permission_callbackanyA custom callback MFA for permission checking. 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 nil, inherits from the section-level permission_callback. 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 the gen_api section-level permission_callback (which defaults to nil).
choose_node_modeanyNode selection strategy for this endpoint. Overrides the section-level setting. - :random - Select a random node - :hash - Hash-based selection using request_type - {:hash, key} - Hash-based selection using the specified argument key - :round_robin - Round-robin across nodes
nodesanyTarget nodes for this endpoint. Overrides the section-level setting. Can be: - A list of node atoms: [:"node1@host", :"node2@host"] - An MFA tuple: {ClusterHelper, :get_nodes, [:chat]} - :local - Execute on the local node
retryanyRetry configuration when execution fails. - nil (default) - No retry - 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
versionString.tVersion string for this API endpoint. Used for API versioning. Defaults to the gen_api section-level version (which defaults to "0.0.1"). Format: Should follow Semantic Versioning (e.g., "1.0.0"). If not set at the resource level, inherits from the domain-level version.
mfaanyExplicit MFA tuple to call instead of the auto-generated one. When specified, this overrides the default MFA derived from the resource module and action name. The auto-generated MFA is {ResourceModule, :action_name, []}. The actual function is called with the converted arguments plus request_info (if enabled). Example: {MyApp.Interface.Api, :send_direct_message, []}
arg_typesanyExplicit argument types map. When provided, overrides the auto-derived arg_types from the Ash action's attributes and arguments. Simple Format (Backward Compatible) - Uses just the type atom: elixir arg_types: %{"user_id" => :string, "age" => :num} Extended Format (New) - Uses a keyword list with :type and optional parameters: elixir arg_types: %{ "user_id" => [type: :string, max_bytes: 255, allow_nil?: true], "age" => [type: :num, default_value: 18], "tags" => [type: :list_string, max_items: 10, max_item_bytes: 100] } Extended Format Options: - type: - Required. The argument type (:string, :num, :boolean, etc.) - allow_nil?: - Optional. When true, allows nil values (default: false) - default_value: - Optional. Default value if argument is missing from request - max_bytes: - For :string type, max byte size - max_items: - For list/map types, max number of items - max_item_bytes: - For :list_string, max bytes per item Nil Attribute Support: When an Ash attribute has allow_nil? true, the auto-derived arg_types will use the extended format with allow_nil?: true. This allows PhoenixGenApi clients to properly handle optional fields. Example: %{"user_id" => :string, "count" => :num, "tags" => {:list_string, 1000, 50}}
arg_ordersany:mapExplicit argument order list, or :map to derive from arg_types keys. - A list of argument name strings overrides the auto-derived order. Example: ["user_id", "content", "file_id"] - :map (default) derives arg_orders from the arg_types map keys.
disabledbooleanfalseWhen true, this endpoint is disabled and will not be included in the generated FunConfig list. Useful for temporarily disabling an endpoint without removing its configuration.
code_interface?boolean | nilWhether to generate a code interface function for this specific action. When true, a function matching the action name will be defined on the resource module that calls the action through the Ash framework. When nil (the default), inherits from the section-level code_interface? setting. Set to false to disable code interface generation for this action while keeping it enabled for others.
result_encoderanyHow to encode the result returned from the action MFA call. - :struct — Return the Ash resource struct as-is (default behavior) - :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. When nil (the default), inherits from the section-level result_encoder setting. The encoding is applied in the generated code interface functions. 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 the gen_api section-level result_encoder (which defaults to :struct).

Introspection

Target: AshPhoenixGenApi.Resource.ActionConfig

gen_api.mfa

mfa name

Configures a standalone PhoenixGenApi MFA endpoint.

Unlike action entities which map an Ash resource action to a FunConfig, mfa entities define endpoints that call an arbitrary MFA function directly. This is useful for exposing custom functions that don't map to standard Ash CRUD actions, such as utility endpoints, batch operations, or service-to-service calls.

Both request_type and mfa are required. arg_types must be explicitly provided since there is no Ash action to auto-derive from. arg_orders defaults to :map, which passes arguments as a map with string keys.

Examples

mfa :my_custom do
  request_type "my_custom"
  mfa {MyApp.Interface.Api, :my_custom, []}
  arg_types %{"user_id" => :string}
  timeout 5_000
end
mfa :batch_process do
  request_type "batch_process"
  mfa {MyApp.BatchProcessor, :run, []}
  arg_types %{"items" => {:list_string, 1000, 50}, "mode" => :string}
  arg_orders ["items", "mode"]
  response_type :async
  request_info true
end

Arguments

NameTypeDefaultDocs
nameatomA unique identifier for this MFA endpoint. Used to distinguish this endpoint from others in the same resource.

Options

NameTypeDefaultDocs
request_typeString.tThe PhoenixGenApi request type string used by clients to call this endpoint. Must be unique across all endpoints in the same resource. Example: "my_custom", "batch_process"
mfaanyThe MFA tuple to call when this endpoint is invoked. The function is called with predefined_args ++ converted_args ++ info_args: - predefined_args — the third element of this tuple (e.g., []) - converted_args — request arguments (a map when arg_orders is :map, or positional values when arg_orders is a list) - info_args — request info map if request_info is true Example: {MyApp.Interface.Api, :my_custom, []}
arg_typesanyArgument types map. Required since there is no Ash action to auto-derive from. Keys are argument name strings, values are PhoenixGenApi type atoms/tuples: - :string - String values - {:string, max_bytes} - String with custom max byte size - :num - Numeric values - :boolean - Boolean values - :datetime - ISO 8601 datetime string, auto-converted to DateTime - :naive_datetime - ISO 8601 datetime string, auto-converted to NaiveDateTime - :map - Generic map - {:map, max_items} - Map with max items constraint - :list - Generic list - {:list, max_items} - List with max items constraint - {:list_string, max_items, max_item_length} - List of strings - {:list_num, max_items} - List of numbers Example: %{"user_id" => :string, "count" => :num, "tags" => {:list_string, 1000, 50}, "metadata" => :map, "sent_at" => :datetime}
arg_ordersany:mapArgument order list, or :map to pass arguments as a map (default). - A list of argument name strings specifies positional ordering. Example: ["user_id", "content", "file_id"] - :map (default) passes arguments as a map with string keys.
timeoutanyTimeout in milliseconds for the function call. Defaults to the gen_api section-level timeout (which defaults to 5000). Accepts a positive integer or :infinity.
response_typeatomThe response mode for this endpoint. - :sync - Client waits for the result - :async - Client receives an ack, then the result later - :stream - Client receives streamed chunks - :none - Fire and forget Defaults to the gen_api section-level response_type (which defaults to :async).
request_infobooleanWhether to pass request info (user_id, device_id, request_id) as the last argument to the MFA function. When true, the MFA will receive a map with %{user_id: ..., device_id: ..., request_id: ...} as the last argument. Defaults to the gen_api section-level request_info (which defaults to true).
check_permissionanyPermission check mode for this endpoint. - false - No permission check - :any_authenticated - Requires a valid user_id - {:arg, "arg_name"} - The specified argument must match user_id - {:role, ["admin", "moderator"]} - User must have one of the listed roles Defaults to the gen_api section-level check_permission (which defaults to false).
permission_callbackanyA custom callback MFA for permission checking. 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). When nil, inherits from the section-level permission_callback. 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 the gen_api section-level permission_callback (which defaults to nil).
choose_node_modeanyNode selection strategy for this endpoint. Overrides the section-level setting. - :random - Select a random node - :hash - Hash-based selection using request_type - {:hash, key} - Hash-based selection using the specified argument key - :round_robin - Round-robin across nodes
nodesanyTarget nodes for this endpoint. Overrides the section-level setting. Can be: - A list of node atoms: [:"node1@host", :"node2@host"] - An MFA tuple: {ClusterHelper, :get_nodes, [:chat]} - :local - Execute on the local node
retryanyRetry configuration when execution fails. - nil (default) - No retry - 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
versionString.tVersion string for this API endpoint. Used for API versioning. Defaults to the gen_api section-level version (which defaults to "0.0.1").
disabledbooleanfalseWhen true, this endpoint is disabled and will not be included in the generated FunConfig list. Useful for temporarily disabling an endpoint without removing its configuration.

Introspection

Target: AshPhoenixGenApi.Resource.MfaConfig