GraphQl with absinthe
Tags: graphql, absinthe, elixir, api
Published: 2019-02-14 20:15:56

---
title: GraphQl with absinthe
published: true
description: My journey through GraphQL lands with absinthe
tags: graphql, absinthe, elixir, api
series: graphql
---

There are a ton of graphql tutorials already. This is more like solidifying my understanding of the topic. If you want to learn it, there probably is [better](https://hexdocs.pm/absinthe/overview.html) [resources](https://caskstrength.wordpress.com/2009/07/29/absinthe-what-it-is-how-to-serve-it-the-basics/) to [follow](https://dev.to/timber/a-gentle-introduction-to-graphql-with-elixir-and-phoenix-736).


## Gotcha 1: Everything is a field.

Literally, everything is a "field". What do I mean?
> Each field can be a [predefined scalar value](https://hexdocs.pm/absinthe/Absinthe.Type.Scalar.html) (`string` `boolean`, `float`, `id` or `integer`) or [custom scalar value](https://hexdocs.pm/absinthe/custom-scalars.html)


```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in this scope can be publicly accessible
  query do
    # Defining field `name` with type string
    field :name, :string
  end
end
```

Now we can execute query:

```graphql
query {
  name
}
```

and expect the following output:

```json
{
  "data": {
    "name": null
  }
}
```

There's nothing to it because we haven't told absynthe how to resolve this field. Let's for now tell it to return hardcoded value.

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible
  query do
    # Defining field `name` with type string and resolver for it
    field :name, :string, resolve: fn _, _, _ -> {:ok, "John Doe"} end
  end
end
```

Now executing the same query we get `John Doe` as value for the name field instead of null

```json
{
  "data": {
    "name": "John Doe"
  }
}
```

The resolver callback cannot return an object if the type is provided as a string.

## Gotcha 2: Fields can also be complex structures (list, map)

Often field will contain more complex value than a scalar. Especially if the field is at root level:

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible
  query do
    # Defining field `user` with type `user`
    field :user, :user
  end

  # Custom defined object `user` that has one field `name` and resolver for it
  object :user do
    field :name, :string, resolve: fn _, _, _ -> {:ok, "John Doe"} end
  end
end
```

Now we can execute query:

```graphql
query {
  user {
    name
  }
}
```

and expect the following output:

```json
{
  "data": {
    "user": {
      "name": "John Doe"
    }
  }
}
```

but actually, we get 

```json
{
  "data": {
    "user": {
      "name": null
    }
  }
}
```

Hmmm... so... I can not resolve deeply nested structures. I need to resolve at the root level. At least it seems so.

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible 
  query do
    # Defining field `user` with type `user` and resolver for it
    field :user, :user, resolve: fn _, _, _ -> {:ok, %{name: "John Doe"}} end
  end

  # Custom defined object `user` that has one field `name`
  object :user do
    field :name, :string
  end
end
```

While executing the previous query we now indeed get the expected output:

```json
{
  "data": {
    "user": {
      "name": "John Doe"
    }
  }
}
```

If the parent level doesn't provide field, the child field resolver callback isn't called. So we could get the same effect by combining resolvers from both levels:

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible
  query do
    # Defining field `user` with type `user` and resolver for it
    field :user, :user, resolve: fn _, _, _ -> {:ok, %{name: nil}} end
  end


  # Custom defined object `user` that has one field `name` and resolver for it
  object :user do
    field :name, :string, resolve: fn _, _, _ -> {:ok, "John Doe"} end
  end
 end
```

We can even combine resolvers from both levels and expect the same output:

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible
  query do
    # Defining field `user` with type `user` and resolver for it
    field :user, :user, resolve: fn _, _, _ -> {:ok, %{name: "John"}} end
  end

  # Custom defined object `user` that has one field `name` and resolver for it
  object :user do
    field :name, :string, resolve: fn %{name: name}, _, _ -> {:ok, name <> " Doe"} end
  end
end
```

### Gotcha 2.1: Fields can also be complex structures

All tutorials I were looking into, were tightly coupled with ecto, so it somehow slipped my attention, how exactly field structures were working. Here you could see the even deeper nested structure. Each `user` have `permissions` map field with two values `is_admin?` and `role`. 

```elixir
defmodule MyApp.Schema do
  use Absinthe.Schema

  # All fields in the query scope can be publicly accessible
  query do
    # Defining field `user` with type `user` and resolver for it
    field :user, :user, resolve: &resolve_user/3
  end

  # Custom defined object `user` that has two fields `name` and `permissions` and resolver for the latter
  object :user do
    field :name, :string
    field :permissions, :permissions, resolve: &resolve_permisisons/3
  end

  # Custom defined object `permissions` that has two fields
  object :permissions do
    field :is_admin, :boolean
    field :role, :string
  end

  defp resolve_user(_, _, _) do
    # Here as well could happen DB query, or reading data from any imaginable data source possible
    # For now simulating that JSON value is retrieved from a data source in permissions field
    user = %{
      name: "John Doe",
      permissions: Jason.encode!(%{is_admin: true, role: "admin"})
    }

    {:ok, user}
  end

  defp resolve_permisisons(%{permissions: permissions}, _, _) do
    Jason.decode(permissions, keys: :atoms!)
  end
end
```

Needless to say that such hierarchies could go as deep as necessary. There are some security concerns regarding [depth](https://www.howtographql.com/advanced/4-security/).

It is possible to retrieve data from one data source, and while resolving its children, mutate its values (in this case decoding JSON), but even reach in other data source for extra data. Be careful tho, [1+n](https://medium.com/@bretdoucette/n-1-queries-and-how-to-avoid-them-a12f02345be5) can creep very fast.

## In conclusion

From the graphql perspective, there isn't some special root elements, root objects or something like that. Everything is a field, and you have complete control over what (string, integer, some custom scalar value or object) each field represents and how data for a specific field is retrieved and processed.

As with erlang/elixir itself, such simple concept as processes with mailboxes have evolved into such stable environments, the graphql went similar path, by choosing to implement as little new concepts as possible. When right building blocks are refined, solutions on those blocks can be built not only faster, but also more stable.

I sort of understand hype train around graphql. Will see how my journey through mutations, paginations, and other seemingly more complex use cases will go, but that's a story for another day.

P.S. Feedback is welcome