# `AshNeo4j.DataLayer`
[🔗](https://github.com/diffo-dev/ash_neo4j/blob/v0.10.1/lib/data_layer.ex#L5)

Ash DataLayer for Neo4j.

## Errors (#372)

Every error the behaviour callbacks (`create`/`update`/`destroy`/`run_query`/…)
return is a **typed struct** — never a bare string (a string becomes
`Ash.Error.Unknown.UnknownError`: unclassified and only substring-matchable).
Pick by meaning:

  * **Server/Bolt error** → `AshNeo4j.Error.Neo4j` (classifies by category).
  * **Deliberate refusal** ("can't push this down / won't render it") → the
    `AshNeo4j.Error.Unsupported*` / `Requires*` family (`class: :invalid`).
  * **The node/edge we expected to act on wasn't there** → reuse Ash's own:
    `Ash.Error.Changes.StaleRecord` (the record is gone / a guard no longer holds)
    or `Ash.Error.Invalid.Unavailable` (a preservation guard blocked a destroy).
  * **An internal invariant we couldn't satisfy** (unexpected input shape, a
    relationship/aggregate path that won't resolve) → `AshNeo4j.Error.Internal`
    (`class: :unknown`).

Two standing rules: **reuse an Ash error before inventing an `AshNeo4j.Error.*`**,
and **return, never raise**, from the behaviour path. Internal sentinels between
helpers (e.g. `{:error, :nothing_deleted}` from `run_expecting_deletions/2`) are
atoms, not strings, and are converted to a typed error before escaping. The
contract is enforced by `test/error_contract_test.exs`.

`Ash.Type` callbacks (cast/dump/load) are a separate contract — they follow Ash's
type-error convention (message strings), not this one.

# `cast_attribute`

# `neo4j`
*macro* 

# `project_traversal`

```elixir
@spec project_traversal(module(), [Ash.Resource.Record.t()], list()) :: %{
  optional(any()) =&gt; any()
}
```

Read-time polymorphic projection (#329): for each source record, follows
`chain` to the reached node(s) in one query (`related_nodes/4`) and projects
each reached node to its concrete world via `AshNeo4j.worlds/1`.

Returns `%{source_pk_value => projected}` where `projected` is the concrete
record, an `AshNeo4j.Unknown` (a node was reached but its labels resolve to no
loaded world), or `nil` (genuinely nothing reached). v1 is single-valued.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
