ash/documentation/how-to/use-resources-without-data-layers.md

97 lines
2.8 KiB
Markdown
Raw Normal View History

# 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
```elixir
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.
```elixir
# 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.
```elixir
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!