Generates a custom Ash.Type for an embedded Ash resource stored in DynamoDB.
DynamoDB stores embedded resources as plain maps with string keys. When Ash reads
them back, it tries to "load through" embedded attributes expecting Ash structs with
__metadata__. This causes a KeyError on :__metadata__.
This macro generates a type that handles the DynamoDB map ↔ struct conversion and
sets cast_in_query?: false to prevent Ash from loading through.
Usage
First, define your embedded resource:
defmodule MyApp.Tag do
use Ash.Resource, data_layer: :embedded
attributes do
attribute :name, :string, allow_nil?: false, public?: true
attribute :color, :string, allow_nil?: false, public?: true
end
endThen create a type module for it:
defmodule MyApp.Types.Tag do
use AshDynamo.EmbeddedType, resource: MyApp.Tag
endFinally, use the type in your DynamoDB-backed resource:
defmodule MyApp.Post do
use Ash.Resource,
data_layer: AshDynamo.DataLayer,
domain: MyApp.Domain
attributes do
attribute :tags, {:array, MyApp.Types.Tag}, allow_nil?: false
end
endWhy is this needed?
When using {:array, MyApp.Tag} directly (the embedded resource), Ash attempts to
"load through" the embedded attributes after reading from the data layer. This works
with data layers like AshPostgres that return properly cast embedded structs, but
DynamoDB returns plain maps with string keys, causing:
** (KeyError) key :__metadata__ not found in: %{"name" => "elixir", "color" => "purple"}The custom type solves this by:
- Casting string-keyed maps from DynamoDB into the embedded resource struct (
cast_stored/2) - Casting input maps (atom or string keys) into the struct (
cast_input/2) - Dumping the struct back to a plain map for DynamoDB storage (
dump_to_native/2) - Returning
cast_in_query?: falseto prevent Ash from loading through the type
Type Validation
Both cast_input/2 and cast_stored/2 validate each field through their respective
Ash.Type casting functions. If any field fails validation, the entire cast returns
:error. This ensures invalid data is caught on both writes and reads — a corrupted
DynamoDB record will raise an error immediately rather than propagating bad data.
ExAws.Dynamo.Encodable
The macro automatically implements the ExAws.Dynamo.Encodable protocol for the
embedded resource, so ExAws can encode it when writing to DynamoDB. The implementation
converts the struct to a plain map containing only the resource's attribute fields.
If you prefer to control the encoding yourself (e.g. to exclude specific fields), you
can add @derive {ExAws.Dynamo.Encodable, only: [:name, :color]} to your embedded
resource module before use AshDynamo.EmbeddedType is called. The macro will skip
the automatic implementation if one already exists.