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
@markkanof
Copy link

markkanof commented Aug 4, 2017

UPDATE:
Please disregard my question below. Everything was working fine, I just made a really dumb mistake and had neglected to export my IntrospectionFragmentMatcher object.

This is fantastic. Thanks for sharing the mix task. Might you be able to share what one of your queries looks like? I'm trying to get a UNION working and am running into some trouble. If I run the query using the Graphiql web based tool it looks like everything comes back as I would expect, but when running the query in my app using Apollo I just get undefined for the query data.

@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