Skip to content

Instantly share code, notes, and snippets.

@tlux
Last active December 2, 2017 15:06
Show Gist options
  • Save tlux/765835296ef3e547cd0e232a0c1eed47 to your computer and use it in GitHub Desktop.
Save tlux/765835296ef3e547cd0e232a0c1eed47 to your computer and use it in GitHub Desktop.
GraphQL: Using fragments on interfaces or unions with Absinthe and Apollo. The Mix task basically generates a JSON file from your Absinthe schema that contains mappings of interfaces/unions to the particular implemented types. This file can subsequently be read by your custom Apollo client.
// Provides an automated solution for the following issue:
// http://dev.apollodata.com/react/initialization.html#fragment-matcher
import {
ApolloClient,
createNetworkInterface,
IntrospectionFragmentMatcher
} from 'apollo-client'
import types from './typemap.json'
// Use a custom fragment matcher to tell Apollo how interface and union types
// used in fragments resolve to implemented types.
const fragmentMatcher = new IntrospectionFragmentMatcher({
introspectionQueryResultData: {
__schema: { types }
}
})
// Define a the GraphQL endpoint.
const networkInterface = createNetworkInterface({
uri: '/graphql'
})
// Build the Apollo client.
export default new ApolloClient({
networkInterface,
fragmentMatcher
})
defmodule Mix.Tasks.Apollo.Gen.Typemap do
@moduledoc """
Provides a task to regenerate the fragment-types.json file which provides
details about GraphQL interfaces and unions that are used by the Apollo
library.
"""
use Mix.Task
alias Absinthe.Schema, as: SchemaUtils
alias Absinthe.Type.Interface
alias Absinthe.Type.Union
# TODO: Replace this with your own Absinthe schema!
alias MyApp.Web.Schema
@shortdoc "Generates a GraphQL typemap.json file for Apollo"
# TODO: Whereever you want your file to be located!
@path Path.join(File.cwd!, "./typemap.json")
@doc false
@spec run([String.t]) :: any
def run(_args) do
data = for type <- SchemaUtils.types(Schema),
type.__struct__ in [Interface, Union] do
identifier = type.__reference__.identifier
implementations = SchemaUtils.concrete_types(Schema, identifier)
kind = case type do
%Interface{} -> "INTERFACE"
%Union{} -> "UNION"
end
possible_types = Enum.map(implementations, &%{"name" => &1.name})
%{
"kind" => kind,
"name" => type.name,
"possibleTypes" => possible_types
}
end
Application.ensure_all_started(:poison)
serialized_data = Poison.encode!(data, pretty: true)
File.write!(@path, serialized_data)
end
end
@mpoeter
Copy link

mpoeter commented Sep 18, 2017

In order for this to work with unions I had to adapt the code a bit:

def run(_args) do
  data =
    for type <- SchemaUtils.types(Schema),
        type.__struct__ in [Interface, Union] do
      # Schema.implementors only supports interface types
      implementations = SchemaUtils.concrete_types(Schema, type)
      kind = case type do
                %Interface{} -> "INTERFACE"
                %Union{} -> "UNION"
              end
      possible_types = Enum.map(implementations, &%{"name" => &1.name})
      %{
        "kind" => kind,
        "name" => type.name,
        "possibleTypes" => possible_types
      }
    end

  Application.ensure_all_started(:poison)
  serialized_data = Poison.encode!(data, pretty: true)
  File.write!(@path, serialized_data)
end

@tlux
Copy link
Author

tlux commented Dec 2, 2017

Thanks for the hint! I fixed that in the original code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment