Grephql - Compile-time GraphQL client with typed Ecto schemas

Hi everyone! I’d like to share Grephql, a compile-time GraphQL client for Elixir.

Motivation

Most existing Elixir GraphQL clients are runtime-only — queries are parsed and sent as plain strings, with responses decoded into plain maps. This means typos, schema mismatches, and type errors only surface at runtime.

Grephql takes a different approach: it parses and validates your GraphQL queries at compile time against your schema, and generates typed Ecto embedded schemas for both responses and variables.

Features

  • Compile-time validation — syntax errors and schema mismatches are caught by mix compile, with line:column positions pointing into your source
  • Deprecation warnings@deprecated fields/arguments/enum values emit compile-time warnings
  • Typed responses — auto-generated Ecto embedded schemas, so result.data.user.name instead of result["data"]["user"]["name"]
  • Typed variables — input validation via Ecto changesets
  • Zero runtime parsing — all GraphQL parsing happens at compile time
  • Req integration — full access to Req’s middleware/plugin system, including Req.Test for testing
  • ~GQL sigil + formatter pluginmix format auto-formats your GraphQL strings
  • Fragments, unions, interfaces, aliases — all supported

Quick example

defmodule MyApp.GitHub do
  use Grephql,
    otp_app: :my_app,
    source: "priv/schemas/github.json",
    endpoint: "https://api.github.com/graphql"

  defgql :get_user, ~GQL"""
    query GetUser($login: String!) {
      user(login: $login) {
        name
        bio
      }
    }
  """
end

# Typed access — no more map key typos
{:ok, result} = MyApp.GitHub.get_user(%{login: "octocat"})
result.data.user.name  #=> "The Octocat"

# Invalid variables are caught before the HTTP call
{:error, %Ecto.Changeset{}} = MyApp.GitHub.get_user(%{})

Testing with Req.Test

# config/test.exs
config :my_app, MyApp.GitHub,
  req_options: [plug: {Req.Test, MyApp.GitHub}]

# In your test
Req.Test.stub(MyApp.GitHub, fn conn ->
  Req.Test.json(conn, %{
    "data" => %{"user" => %{"name" => "Alice", "bio" => "Elixirist"}}
  })
end)

assert {:ok, result} = MyApp.GitHub.get_user(%{login: "alice"})
assert result.data.user.name == "Alice"

Real-world examples

For more real-world examples — including a GitHub API client (5.9MB schema, nested connections, mutations, input objects) and a Shopify Admin API client (10MB schema, products, orders, customers) — see the examples/ directory.

Links

Would love to hear any feedback or questions!

2 Likes

Nice I’ll check it out tomorrow

in the meantime check out prismatic:

Shipped linear sdk with it:

Glad to see the ecosystem expanding!