Created
December 13, 2018 08:19
-
-
Save smilingleo/f90be2b5017a86ec44ab61161dbce650 to your computer and use it in GitHub Desktop.
Delegation GraphQL resolver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* @flow strict | |
*/ | |
import { | |
GraphQLEnumType, | |
GraphQLInterfaceType, | |
GraphQLObjectType, | |
GraphQLList, | |
GraphQLNonNull, | |
GraphQLSchema, | |
GraphQLString, | |
} from '../type'; | |
import { getFriends, getHero, getHuman, getDroid } from './starWarsData'; | |
import { graphql } from '../graphql'; | |
const episodeEnum = new GraphQLEnumType({ | |
name: 'Episode', | |
description: 'One of the films in the Star Wars Trilogy', | |
values: { | |
NEWHOPE: { | |
value: 4, | |
description: 'Released in 1977.', | |
}, | |
EMPIRE: { | |
value: 5, | |
description: 'Released in 1980.', | |
}, | |
JEDI: { | |
value: 6, | |
description: 'Released in 1983.', | |
}, | |
}, | |
}); | |
const characterInterface = new GraphQLInterfaceType({ | |
name: 'Character', | |
description: 'A character in the Star Wars Trilogy', | |
fields: () => ({ | |
id: { | |
type: GraphQLNonNull(GraphQLString), | |
description: 'The id of the character.', | |
}, | |
name: { | |
type: GraphQLString, | |
description: 'The name of the character.', | |
}, | |
friends: { | |
type: GraphQLList(characterInterface), | |
description: | |
'The friends of the character, or an empty list if they have none.', | |
}, | |
appearsIn: { | |
type: GraphQLList(episodeEnum), | |
description: 'Which movies they appear in.', | |
}, | |
secretBackstory: { | |
type: GraphQLString, | |
description: 'All secrets about their past.', | |
}, | |
}), | |
resolveType(character) { | |
if (character.type === 'Human') { | |
return humanType; | |
} | |
if (character.type === 'Droid') { | |
return droidType; | |
} | |
}, | |
}); | |
const humanType = new GraphQLObjectType({ | |
name: 'Human', | |
description: 'A humanoid creature in the Star Wars universe.', | |
fields: () => ({ | |
id: { | |
type: GraphQLNonNull(GraphQLString), | |
description: 'The id of the human.', | |
}, | |
name: { | |
type: GraphQLString, | |
description: 'The name of the human.', | |
}, | |
friends: { | |
type: GraphQLList(characterInterface), | |
description: | |
'The friends of the human, or an empty list if they have none.', | |
resolve: human => getFriends(human), | |
}, | |
appearsIn: { | |
type: GraphQLList(episodeEnum), | |
description: 'Which movies they appear in.', | |
}, | |
homePlanet: { | |
type: GraphQLString, | |
description: 'The home planet of the human, or null if unknown.', | |
}, | |
secretBackstory: { | |
type: GraphQLString, | |
description: 'Where are they from and how they came to be who they are.', | |
resolve() { | |
throw new Error('secretBackstory is secret.'); | |
}, | |
}, | |
}), | |
interfaces: [characterInterface], | |
}); | |
const droidType = new GraphQLObjectType({ | |
name: 'Droid', | |
description: 'A mechanical creature in the Star Wars universe.', | |
fields: () => ({ | |
id: { | |
type: GraphQLNonNull(GraphQLString), | |
description: 'The id of the droid.', | |
}, | |
name: { | |
type: GraphQLString, | |
description: 'The name of the droid.', | |
}, | |
friends: { | |
type: GraphQLList(characterInterface), | |
description: | |
'The friends of the droid, or an empty list if they have none.', | |
resolve: droid => getFriends(droid), | |
}, | |
appearsIn: { | |
type: GraphQLList(episodeEnum), | |
description: 'Which movies they appear in.', | |
}, | |
secretBackstory: { | |
type: GraphQLString, | |
description: 'Construction date and the name of the designer.', | |
resolve() { | |
throw new Error('secretBackstory is secret.'); | |
}, | |
}, | |
primaryFunction: { | |
type: GraphQLString, | |
description: 'The primary function of the droid.', | |
}, | |
}), | |
interfaces: [characterInterface], | |
}); | |
const apiType = new GraphQLObjectType({ | |
name: 'ApiQuery', | |
fields: () => ({ | |
hero: { | |
type: characterInterface, | |
args: { | |
episode: { | |
description: | |
'If omitted, returns the hero of the whole saga. ' + | |
'If provided, returns the hero of that particular episode.', | |
type: episodeEnum, | |
}, | |
}, | |
resolve: (root, { episode }) => getHero(episode), | |
}, | |
human: { | |
type: humanType, | |
args: { | |
id: { | |
description: 'id of the human', | |
type: GraphQLNonNull(GraphQLString), | |
}, | |
}, | |
resolve: (root, { id }) => getHuman(id), | |
}, | |
droid: { | |
type: droidType, | |
args: { | |
id: { | |
description: 'id of the droid', | |
type: GraphQLNonNull(GraphQLString), | |
}, | |
}, | |
resolve: (root, { id }) => getDroid(id), | |
}, | |
}), | |
}); | |
const rawType = new GraphQLObjectType({ | |
name: 'RawQuery', | |
fields: () => ({ | |
hero: { | |
type: characterInterface, | |
args: { | |
episode: { | |
description: | |
'If omitted, returns the hero of the whole saga. ' + | |
'If provided, returns the hero of that particular episode.', | |
type: episodeEnum, | |
}, | |
}, | |
resolve: (root, { episode }) => getHero(episode), | |
}, | |
human: { | |
type: humanType, | |
args: { | |
id: { | |
description: 'id of the human', | |
type: GraphQLNonNull(GraphQLString), | |
}, | |
}, | |
resolve: (root, { id }) => getHuman(id), | |
}, | |
droid: { | |
type: droidType, | |
args: { | |
id: { | |
description: 'id of the droid', | |
type: GraphQLNonNull(GraphQLString), | |
}, | |
}, | |
resolve: (root, { id }) => getDroid(id), | |
}, | |
}), | |
}); | |
/** | |
* compose multiple queries in one Query. In case you don't want to mix them together. | |
*/ | |
const queryType = new GraphQLObjectType({ | |
name: 'Query', | |
fields: () => ({ | |
api: { | |
type: apiType, | |
resolve: delegateResolver(apiType, 'api'), | |
}, | |
raw: { | |
type: rawType, | |
resolve: delegateResolver(rawType, 'raw'), | |
}, | |
}), | |
}); | |
/** | |
* This is the meat of this gist. | |
* | |
*/ | |
const delegateResolver = (objType, name) => { | |
return (root, args, context, info) => { | |
const subSchema = new GraphQLSchema({ | |
query: objType, | |
}); | |
const subSelection = info.operation.selectionSet.selections.find(s => s.name.value === name) | |
const subSource = subSelection.loc.source.body.substring(subSelection.loc.start, subSelection.loc.end) | |
const newSource = subSource.replace(name, 'query'); | |
return graphql( | |
subSchema, | |
newSource, | |
root, | |
context, | |
args, | |
); | |
} | |
} | |
/** | |
* Finally, we construct our schema (whose starting query type is the query | |
* type we defined above) and export it. | |
*/ | |
export const StarWarsSchema = new GraphQLSchema({ | |
query: queryType, | |
types: [humanType, droidType], | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment