ash_graphql/documentation/topics/graphql-generation.md
2024-05-24 15:59:24 -04:00

306 lines
7 KiB
Markdown

# GraphQL Query Generation
Following where we left off from [Getting Started with GraphQL](/documentation/tutorials/getting-started-with-graphql.md), this guide explores what the GraphQL requests and responses look like for different queries defined with the AshGraphql DSL.
All of the following examples apply to queries & mutations places on the domain as well.
## Fetch Data by ID
```elixir
defmodule Helpdesk.Support.Ticket do
use Ash.Resource,
...,
extensions: [
AshGraphql.Resource
]
attributes do
# Add an autogenerated UUID primary key called `:id`.
uuid_primary_key :id
# Add a string type attribute called `:subject`
attribute :subject, :string
end
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :update, :destroy]
end
graphql do
type :ticket
queries do
# create a field called `get_ticket` that uses the `read` read action to fetch a single ticket
get :get_ticket, :read
end
end
end
```
For the `get_ticket` query defined above, the corresponding GraphQL would look like this:
```graphql
query ($id: ID!) {
getTicket(id: $id) {
id
subject
}
}
```
And the response would look similar to this:
```json
{
"data": {
"getTicket": {
"id": "",
"subject": ""
}
}
}
```
Let's look at an example of querying a list of things.
```elixir
graphql do
type :ticket
queries do
# create a field called `get_ticket` that uses the `read` read action to fetch a single ticket
get :get_ticket, :read
# create a field called `list_tickets` that uses the `read` read action to fetch a list of tickets
list :list_tickets, :read
end
end
```
This time, we've added `list :list_tickets, :read`, to generate a GraphQL query for listing tickets.
The request would look something like this:
```graphql
query {
listTickets {
id
subject
}
}
```
And the response would look similar to this:
```json
{
"data": {
"listTickets": [
{
"id": "",
"subject": ""
}
]
}
}
```
## Filter Data With Arguments
Now, let's say we want to add query parameters to `listTickets`. How do we do that?
Consider `list :list_tickets, :read` and the `actions` section:
```elixir
actions do
# Add a set of simple actions. You'll customize these later.
defaults [:read, :update, :destroy]
end
graphql do
type :ticket
queries do
# create a field called `list_tickets` that uses the `read` read action to fetch a list of tickets
list :list_tickets, :read
end
end
```
The second argument to `list :list_tickets, :read` is the action that will be called when the query is run.
In the current example, the action is `:read`, which is the generic Read action.
Let's create a custom action in order to define query parameters for the `listTickets` query.
We'll call this action `:query_tickets`:
```elixir
actions do
defaults [:read, :update, :destroy]
read :query_tickets do
argument :representative_id, :uuid
filter(
expr do
is_nil(^arg(:representative_id)) or representative_id == ^arg(:representative_id)
end
)
end
end
graphql do
type :ticket
queries do
# create a field called `list_tickets` that uses the `:query_tickets` read action to fetch a list of tickets
list :list_tickets, :query_tickets
end
end
```
In the `graphql` section, the `list/2` call has been changed, replacing the `:read` action with `:query_tickets`.
The GraphQL request would look something like this:
```graphql
query ($representative_id: ID) {
list_tickets(representative_id: $representative_id) {
id
representative_id
subject
}
}
```
## Mutations and Enums
Now, let's look at how to create a ticket by using a GraphQL mutation.
Let's say you have a Resource that defines an enum-like attribute:
```elixir
defmodule Helpdesk.Support.Ticket do
use Ash.Resource,
...,
extensions: [
AshGraphql.Resource
]
attributes do
uuid_primary_key :id
attribute :subject, :string
attribute :status, :atom, constraints: [one_of: [:open, :closed]]
end
actions do
defaults [:create, :read, :update, :destroy]
end
graphql do
type :ticket
queries do
get :get_ticket, :read
end
mutations do
create :create_ticket, :create
end
end
end
```
Above, the following changes have been added:
1. In the `attributes` section, the `:status` attribute has been added.
2. In the `actions` section, the `:create` action has been added.
3. The `:create_ticket` mutation has been defined in the new `graphql.mutations` section.
The `:status` attribute is an enum that is constrained to the values `[:open, :closed]`.
When used in conjunction with AshGraphql, a GraphQL enum type called `TicketStatus` will be generated for this attribute.
The possible GraphQL values for `TicketStatus` are `OPEN` and `CLOSED`.
See [Use Enums with GraphQL](/documentation/guides/use-enums-with-graphql.md) for more information.
We can now create a ticket with the `createTicket` mutation:
```graphql
mutation ($input: CreateTicketInput!) {
createTicket(input: $input) {
result {
id
subject
status
}
errors {
code
fields
message
shortMessage
vars
}
}
}
```
**Note**
- The resulting ticket data is wrapped in AshGraphql's `result` object.
- Validation errors are wrapped in a list of error objects under `errors`, also specified in the query.
AshGraphql does this by default instead of exposing errors in GraphQL's standard `errors` array.
This behavior can be changed by setting `root_level_errors? true` in the `graphql` section
of your Ash domain module:
```elixir
defmodule Helpdesk.Support do
use Ash.Domain, extensions: [AshGraphql.Domain]
graphql do
root_level_errors? true
end
end
```
If we were to run this mutation in a test, it would look something like this:
```elixir
input = %{
subject: "My Ticket",
status: "OPEN"
}
resp_body =
post(conn, "/api/graphql", %{
query: query,
variables: %{input: input}
})
|> json_response(200)
```
Notice that the `status` attribute is set to `"OPEN"` and not `"open"`. It is important that the value of the `status` be uppercase.
This is required by GraphQL enums. AshGraphql will automatically convert the value to the correct case.
The response will look something like this:
```json
{
"data": {
"createTicket": {
"result": {
"id": "b771e433-0979-4d07-a280-4d12373849aa",
"subject": "My Ticket",
"status": "OPEN"
}
}
}
}
```
Again, AshGraphql will automatically convert the `status` value from `:open` to `"OPEN"`.
## More GraphQL Docs
If you haven't already, please turn on the documentation tag for AshGraphql. Tags can be controlled
at the top of the left navigation menu, under "Including Libraries:".
- [Getting Started With GraphQL](/documentation/tutorials/getting-started-with-graphql.md)
- `AshGraphql.Domain`