ash_graphql/documentation/how_to/use-unions-with-graphql.md

52 lines
1.5 KiB
Markdown
Raw Normal View History

# Use Unions with GraphQL
Unions must be defined with `Ash.Type.NewType`:
```elixir
defmodule MyApp.Armor do
use Ash.Type.NewType, subtype_of: :union, constraints: [
types: [
plate: [
# This is an embedded resource, with its own fields
improvement!: port AshGraphql to Ash 3.0 (#123) Step 1: update Ash Step 2: mass rename Api to Domain Step 3: Ash.Query.expr -> Ash.Expr.expr Also change ref interpolation Step 4: remove all warnings Step 5: remove registries from tests Step 6: fix filter Step 7: private? -> !public? Step 8: Ash.Calculation -> Ash.Resource.Calculation Step 9: use depend_on_resources/1 -> resources/1 Step 10: add Domain to all resources Step 11: use Ash module for all actions Step 12: add public? true all around Step 13: remove verbose? from options passed during Domain calls Step 14: add simple_sat Step 15: Ash.ErrorKind is no more, so remove code from errors Step 16: sprinkle default_accept :* around tests Step 17: replace Ash.Changeset.new/2 with Ash.Changeset.for_* Step 18: calculation fixups - Context is now a struct and arguments go under the arguments key - Function based calculations receive a list of records - Add a select to query-based loads - select -> load Step 19: pass the correct name to pass the policy in tests Step 20: Ash.Query.new/2 is no more Step 21: add AshGraphql.Resource.embedded? utility function Use that instead of Ash.Type.embedded_type?(resource_or_type) since resources are not types anymore Step 22: handle struct + instance_of: Resource in unions Resources are not type anymore so they need to be passed this way in unions Step 23: ensure we only check GraphQL actions for pagination All reads are now paginated by default, so this triggered a compilation error Step 24: swap arguments for sort on calculations Step 25: remove unused debug? option
2024-04-02 07:03:06 +13:00
type: :struct,
constraints: [MyApp.Armor.Plate]
],
chain_mail: [
# And so is this
improvement!: port AshGraphql to Ash 3.0 (#123) Step 1: update Ash Step 2: mass rename Api to Domain Step 3: Ash.Query.expr -> Ash.Expr.expr Also change ref interpolation Step 4: remove all warnings Step 5: remove registries from tests Step 6: fix filter Step 7: private? -> !public? Step 8: Ash.Calculation -> Ash.Resource.Calculation Step 9: use depend_on_resources/1 -> resources/1 Step 10: add Domain to all resources Step 11: use Ash module for all actions Step 12: add public? true all around Step 13: remove verbose? from options passed during Domain calls Step 14: add simple_sat Step 15: Ash.ErrorKind is no more, so remove code from errors Step 16: sprinkle default_accept :* around tests Step 17: replace Ash.Changeset.new/2 with Ash.Changeset.for_* Step 18: calculation fixups - Context is now a struct and arguments go under the arguments key - Function based calculations receive a list of records - Add a select to query-based loads - select -> load Step 19: pass the correct name to pass the policy in tests Step 20: Ash.Query.new/2 is no more Step 21: add AshGraphql.Resource.embedded? utility function Use that instead of Ash.Type.embedded_type?(resource_or_type) since resources are not types anymore Step 22: handle struct + instance_of: Resource in unions Resources are not type anymore so they need to be passed this way in unions Step 23: ensure we only check GraphQL actions for pagination All reads are now paginated by default, so this triggered a compilation error Step 24: swap arguments for sort on calculations Step 25: remove unused debug? option
2024-04-02 07:03:06 +13:00
type: :struct,
constraints: [instance_of: MyApp.Armor.ChainMail]
],
custom: [
type: :string
]
]
]
use AshGraphql.Type
# Add this to define the union in ash_graphql
def graphql_type(_), do: :armor
end
```
By default, the type you would get for this on input and output would look something like this:
```
type Armor = {plate: {value: Plate}} | {chain_mail: {value: ChainMail}} | {custom: {value: String}}
```
We do this by default to solve for potentially ambiguous types. An example of this might be if you had multiple different types of strings in a union, and you wanted the client to be able to tell exactly which type of string they'd been given. i.e `{social: {value: "555-55-5555"}} | {phone_number: {value: "555-5555"}}`.
However, you can clean the type in cases where you have no such conflicts by by providing
```elixir
# Put anything in here that does not need to be named/nested with `{type_name: {value: value}}`
def graphql_unnested_unions(_constraints), do: [:plate, :chain_mail]
```
Which, in this case, would yield:
```
type Armor = Plate | ChainMail | {custom: {value: String}}
```