README.md

![Tests](https://github.com/TheRealReal/graphql_client/actions/workflows/ci.yml/badge.svg)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)

# Graphql Client

A client-side GraphQL library.

## Installation

Add `graphql_client` to you list of dependencies:

```elixir
def deps do
  [{:graphql_client, "~> 0.1"}]
end
```

**Creating a backend**

Now, you need to implement the `GraphQL.Client` behaviour:

```elixir
defmodule MyClient do
  @behaviour GraphQL.Client

  def execute_query(query, variables, options) do
    # your implementation
  end
end
```

**Configuring the client**

In your configuration, set it as your backend:

```elixir
config :graphql_client, backend: MyClient
```

Now, any call to `GraphQL.Client` will use the configured backend.

## Usage
### GraphQL as code

To build queries, you can `import` all functions from `GraphQL.QueryBuilder`.

A simple query, like this one:

```graphql
query User($slug: String! = "*"){
  user(slug: $slug){
    id
    email
}
```

Can be built using the following snippet:

```elixir
import GraphQL.QueryBuilder

user_query = query("User", %{slug: {"String!"}, "*"}, [
  field(:user, %{slug: :"$slug"}, [
    field(:id),
    field(:email)
  ])
])
```

Now, the `user_query` variable contains a _representation_ of this GraphQL operation. If you inspect it, you'll see this:

```elixir
%GraphQL.Query{
  fields: [
    %GraphQL.Node{
      alias: nil,
      arguments: %{slug: :"$slug"},
      name: :user,
      node_type: :field,
      nodes: [
        %GraphQL.Node{
          alias: nil,
          arguments: nil,
          name: :id,
          node_type: :field,
          nodes: nil,
          type: nil
        },
        %GraphQL.Node{
          alias: nil,
          arguments: nil,
          name: :email,
          node_type: :field,
          nodes: nil,
          type: nil
        }
      ],
      type: nil
    }
  ],
  fragments: [],
  name: "User",
  operation: :query,
  variables: [
    %GraphQL.Variable{
      default_value: "*",
      name: :slug,
      type: "String!"
    }
  ]
}
```

But most of the time you'll not need to handle this directly.


### Executing queries

To execute this query, you can now call the `GraphQL.Client` and use this query directly:

```elixir
GraphQL.Client.execute(user_query, %{slug: "some-user"})
```

From the POV of the code that it's calling, it doesn't know if this client is using HTTP, smoke signals or magic.

All you know is that this function will always return a `%GraphQL.Response{}` struct.


To get the actual text body, you can use `GraphQL.Encoder.encode/1` function:

```
iex> user_query |> GraphQL.Encoder.encode() |> IO.puts()
query User($slug: String! = "*") {
  user(slug: $slug) {
    id
    email
  }
}
:ok
```

### The Query Registry

The end goal is to merge different queries into one operation and the query registry does exactly that.

It will accumulate queries, variables and resolvers (yes, resolvers!), merge them, and then execute resolvers with an accumulator.

```elixir
user_query = query(...)
product_query = query(...)

user_resolver = fn response, acc ->
  # do something with the response and return the updated accumulator
  updated_acc
end

registry = QueryRegistry.new("BigQuery")

result = 
  registry
  |> QueryRegistry.add_query(user_query, user_variables,[user_resolver])
  |> QueryRegistry. add_query(product_query, product_variables)
  |> QueryRegistry.execute(%{}, options)

```

A resolver function must accept two parameters: a `%GraphQL.Response{}` struct and the accumulator defined by the query registry.

### Testing

The `%GraphQL.Response{}` is the only thing clients must return, and that we can configure the backend via config files.

Internally, during tests, the backend will be changed to `LocalBackend`, that uses an Agent process to store responses.

Call `GraphQL.LocalBackend.start_link/0` on your `test_helper.exs` file.

Now you can use the `GraphQL.LocalBackend.expect/1` function:

```elixir
import GraphQL.LocalBackend, only: [expect: 1]
alias GraphQL.Response

test "my test" do
  my_registry = QueryRegistry.new(...)
  response = Response.success(%{field: "value"})
  expect(my_registry, response)
  assert 1 == 1
end
```

If you need to inspect and assert the query and variables, you can pass a function:

```elixir
import GraphQL.LocalBackend, only: [expect: 1]
alias GraphQL.Response

test "my test" do
  my_registry = QueryRegistry.new(...)

  expect(my_registry, fn query, _variables, _options ->
    assert query == expected_query
    Response.success(%{field: "value"})
  end)
  assert 1 == 1
end
```

## Code of Conduct

This project  Contributor Covenant version 2.1. Check [CODE_OF_CONDUCT.md](/CODE_OF_CONDUCT.md) file for more information.

## License

`graphql_client` source code is released under Apache License 2.0.

Check [NOTICE](/NOTICE) and [LICENSE](/LICENSE) files for more information.