-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
A user of the Python port of GraphQL.js recently came up with the following question, which I think should be better discussed here:
They are given a schema object that contains an interface (built using SDL). Now they want to programmatically (dynamically) add concrete types implementing this interface to the schema. How would they do that?
Of course, you could instead add the concrete types to the SDL and build the schema from that, or you could instead build a query type and the interface type programmatically, add the concrete types, and then build the schema from the query type. But it would be nice if you could also use the mixed approach, creating the schema first, and then adding the types.
However, adding the concrete types to an existing schema is tricky, because you need to do three things:
- Add the type to the
_type_map.- Ok, this is simple, since you can access the
_type_mapwithgetTypeMap().
- Ok, this is simple, since you can access the
- Add the type to the
_implementations_mapfor the interface.- This is is a problem because you cannot access the private
_implementations_mapor add a new key to this map. We have thegetImplementations()method, but if the interface is not yet implemented in the given schema, the returned object cannot be used for updates, and there is nosetImplementations()method.
- This is is a problem because you cannot access the private
- If the schema was already validated, reset the
_sub_type_mapcache.- This is also problematic because
_sub_type_mapis private and there is no official way to reset it.
- This is also problematic because
So the question is:
- Should we provide an
add_type()method (caring for all of the above) or alternatively, asetImplementations()and some kind ofreset()method that would reset the_sub_type_map, so that you can subsequently add types to an already existing schema?
To illustrate the problem, here is the code example from the original issue translated from Python to JavaScript:
// Create a schema with an interface via SDL
const sdl = `
enum Episode { NEWHOPE, EMPIRE, JEDI }
interface Character {
id: String!
name: String
friends: [Character]
appearsIn: [Episode]
}
type Query {
hero(episode: Episode): Character
}
`;
const schema = buildSchema(sdl);
// Add concrete types programmatically
const CHARACTERS = ["Human", "Droid", "Animal", "Fungus", "Alien"];
const characterInterface = schema.getType("Character");
const episodeClass = schema.getType("Episode");
const query = schema.getType("Query");
const const typeMap = schema.getTypeMap();
const implementations = { objects: [], interfaces: [] };
schema._implementationsMap[characterInterface.name] = implementations;
for (const character of CHARACTERS) {
const concreteCharacter = new GraphQLObjectType({
name: character,
interfaces: [characterInterface],
fields: {
id: { type: new GraphQLNonNull(GraphQLString) },
name: { type: GraphQLString },
friends: { type: new GraphQLList(characterInterface) },
appearsIn: { type: new GraphQLList(episodeClass) },
primaryFunction: { type: GraphQLString },
},
});
typeMap[character] = concreteCharacter;
implementations.objects.push(concreteCharacter);
const name = character.toLowerCase();
query._fields[name] = {
name,
type: concreteCharacter,
args: [{ name: "id", type: new GraphQLNonNull(GraphQLString) }],
};
}
schema._subTypeMap = {}; // need to reset this because we added types
console.log(printSchema(schema));The code above should print this result.