View Source Use Resources without Data Layers
If you don't explicitly set a data layer, then a resource will use Ash.DataLayer.Simple
. In this way, technically, a resource always has a data layer. Ash.DataLayer.Simple
is a data layer that does no persistence, but instead validates and returns structs for mutating actions, and must be manually provided with data to use.
Example
defmodule MyApp.Person do
use Ash.Resource
# notice no data layer is configured
attributes do
uuid_primary_key :id
attribute :name, :string, allow_nil?: false, public?: true
attribute :points, :integer, allow_nil?: false, public?: true
end
actions do
read :read do
prepare MyApp.FetchPeople
end
create :create do
accept [:some]
end
end
end
defmodule MyApp.FetchPeople do
use Ash.Resource.Preparation
@fake_people [
%MyApp.Person{
id: Ash.UUID.generate(),
name: "Joe Armstrong",
points: 10000
},
%MyApp.Person{
id: Ash.UUID.generate(),
name: "José Valim",
points: 10000
}
]
def prepare(query, _, _) do
Ash.Query.before_action(query, fn query ->
case fetch_data(query) do
{:ok, data} ->
Ash.DataLayer.Simple.set_data(query, data)
{:error, error} ->
Ash.Query.add_error(query, SomeBuiltinOrCustomAshError.exception(...))
end
end)
end
defp fetch_data(_query) do
# you could fetch them from an external API here, but for this example
# we will just return some static data.
# Be sure to return instances of the resource!
{:ok, @fake_people}
end
end
Usage
They are used in exactly the same way as regular resources
Create/Update/Destroy
In the example below, we create one, although it is not persisted anywhere and will not be returned when reading. However, you could do custom persistence. If, for example, we were reading from an external API, you might post to an API in an after_action hook on the create.
# You can construct changeset over them
changeset =
Ash.Changeset.for_create(MyApp.Person, :create, %{name: "Dave Thomas", points: 10000})
# This will return the structs by default
# Although you are free to do custom persistence in your resource changes
Ash.create!(changeset)
# %MyApp.FetchComplexResource{...}
Reads
When reading, you can use the resource as you would any other resource.
Resource
|> Ash.Query.filter(contains(name, "José"))
|> Ash.read!()
#=> [%MyApp.Person{name: "José Valim", points: 10000}]
Notice how consumers of your resource (generally) don't need to care if the data is coming from a database, an external API, or static data somewhere. They get the same API, and you can do things like pagination, sorting, etc. in the same way. You can even add the AshGraphql
or AshJsonApi
extension to expose an external API in your own API!