documentation/topics/graphql-generation.md

# GraphQL Query Generation

## Fetch Data by ID

Following where we left off from [Getting Started with GraphQL](/documentation/tutorials/getting-started-with-graphql.md), we'll explore what the GraphQL
requests and responses look like for different queries defined with the AshGraphql DSL.

```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`