@@ -52,7 +52,7 @@ public class GraphQLSchema {
5252 private final GraphQLObjectType mutationType ;
5353 private final GraphQLObjectType subscriptionType ;
5454 private final GraphQLObjectType introspectionSchemaType ;
55- private final ImmutableSet <GraphQLType > additionalTypes ;
55+ private final ImmutableSet <GraphQLNamedType > additionalTypes ;
5656 private final GraphQLFieldDefinition introspectionSchemaField ;
5757 private final GraphQLFieldDefinition introspectionTypeField ;
5858 // we don't allow modification of "__typename" - it's a scalar
@@ -219,7 +219,66 @@ public GraphQLObjectType getIntrospectionSchemaType() {
219219 return introspectionSchemaType ;
220220 }
221221
222- public Set <GraphQLType > getAdditionalTypes () {
222+ /**
223+ * Returns the set of "additional types" that were provided when building the schema.
224+ * <p>
225+ * During schema construction, types are discovered by traversing the schema from multiple roots:
226+ * <ul>
227+ * <li>Root operation types (Query, Mutation, Subscription)</li>
228+ * <li>Directive argument types</li>
229+ * <li>Introspection types</li>
230+ * <li>Types explicitly added via {@link Builder#additionalType(GraphQLNamedType)}</li>
231+ * </ul>
232+ * <p>
233+ * Additional types are types that are not reachable via any of the automatic traversal paths
234+ * but still need to be part of the schema. The most common use case is for interface
235+ * implementations that are not directly referenced elsewhere.
236+ * <p>
237+ * <b>Types that do NOT need to be added as additional types:</b>
238+ * <ul>
239+ * <li>Types reachable from Query, Mutation, or Subscription fields</li>
240+ * <li>Types used as directive arguments (these are discovered via directive traversal)</li>
241+ * </ul>
242+ * <p>
243+ * <b>When additional types ARE typically needed:</b>
244+ * <ul>
245+ * <li><b>Interface implementations:</b> When an interface is used as a field's return type,
246+ * implementing object types are not automatically discovered because interfaces do not
247+ * reference their implementors. These need to be added so they can be resolved at runtime
248+ * and appear in introspection.</li>
249+ * <li><b>SDL-defined schemas:</b> When building from SDL, the {@link graphql.schema.idl.SchemaGenerator}
250+ * automatically detects types not connected to any root and adds them as additional types.</li>
251+ * <li><b>Programmatic schemas with type references:</b> When using {@link GraphQLTypeReference}
252+ * to break circular dependencies, the actual type implementations may need to be provided
253+ * as additional types.</li>
254+ * </ul>
255+ * <p>
256+ * <b>Example - Interface implementation not directly referenced:</b>
257+ * <pre>{@code
258+ * // Given this schema:
259+ * // type Query { node: Node }
260+ * // interface Node { id: ID! }
261+ * // type User implements Node { id: ID!, name: String }
262+ * //
263+ * // User is not directly referenced from Query, so it needs to be added:
264+ * GraphQLSchema.newSchema()
265+ * .query(queryType)
266+ * .additionalType(GraphQLObjectType.newObject().name("User")...)
267+ * .build();
268+ * }</pre>
269+ * <p>
270+ * <b>Note:</b> There are no restrictions on what types can be added via this mechanism.
271+ * Types that are already reachable from other roots can also be added without causing
272+ * errors - they will simply be present in both the type map (via traversal) and this set.
273+ * After schema construction, use {@link #getTypeMap()} or {@link #getAllTypesAsList()} to get
274+ * all types in the schema regardless of how they were discovered.
275+ *
276+ * @return an immutable set of types that were explicitly added as additional types
277+ *
278+ * @see Builder#additionalType(GraphQLNamedType)
279+ * @see Builder#additionalTypes(Set)
280+ */
281+ public Set <GraphQLNamedType > getAdditionalTypes () {
223282 return additionalTypes ;
224283 }
225284
@@ -686,7 +745,7 @@ public static class Builder {
686745 private final Set <GraphQLDirective > additionalDirectives = new LinkedHashSet <>(
687746 asList (Directives .IncludeDirective , Directives .SkipDirective )
688747 );
689- private final Set <GraphQLType > additionalTypes = new LinkedHashSet <>();
748+ private final Set <GraphQLNamedType > additionalTypes = new LinkedHashSet <>();
690749 private final List <GraphQLDirective > schemaDirectives = new ArrayList <>();
691750 private final List <GraphQLAppliedDirective > schemaAppliedDirectives = new ArrayList <>();
692751
@@ -722,16 +781,72 @@ public Builder codeRegistry(GraphQLCodeRegistry codeRegistry) {
722781 return this ;
723782 }
724783
725- public Builder additionalTypes (Set <GraphQLType > additionalTypes ) {
784+ /**
785+ * Adds multiple types to the set of additional types.
786+ * <p>
787+ * Additional types are types that may not be directly reachable by traversing the schema
788+ * from the root operation types (Query, Mutation, Subscription), but still need to be
789+ * included in the schema. The most common use case is for object types that implement
790+ * an interface but are not directly referenced as field return types.
791+ * <p>
792+ * <b>Example - Adding interface implementations:</b>
793+ * <pre>{@code
794+ * // If Node interface is used but User/Post types aren't directly referenced:
795+ * builder.additionalTypes(Set.of(
796+ * GraphQLObjectType.newObject().name("User").withInterface(nodeInterface)...,
797+ * GraphQLObjectType.newObject().name("Post").withInterface(nodeInterface)...
798+ * ));
799+ * }</pre>
800+ * <p>
801+ * <b>Note:</b> There are no restrictions on what types can be added. Types already
802+ * reachable from root operations can be added without causing errors - they will
803+ * simply exist in both the traversed type map and this set.
804+ *
805+ * @param additionalTypes the types to add
806+ *
807+ * @return this builder
808+ *
809+ * @see GraphQLSchema#getAdditionalTypes()
810+ */
811+ public Builder additionalTypes (Set <? extends GraphQLNamedType > additionalTypes ) {
726812 this .additionalTypes .addAll (additionalTypes );
727813 return this ;
728814 }
729815
730- public Builder additionalType (GraphQLType additionalType ) {
816+ /**
817+ * Adds a single type to the set of additional types.
818+ * <p>
819+ * Additional types are types that may not be directly reachable by traversing the schema
820+ * from the root operation types (Query, Mutation, Subscription), but still need to be
821+ * included in the schema. The most common use case is for object types that implement
822+ * an interface but are not directly referenced as field return types.
823+ * <p>
824+ * <b>Note:</b> There are no restrictions on what types can be added. Types already
825+ * reachable from root operations can be added without causing errors.
826+ *
827+ * @param additionalType the type to add
828+ *
829+ * @return this builder
830+ *
831+ * @see GraphQLSchema#getAdditionalTypes()
832+ * @see #additionalTypes(Set)
833+ */
834+ public Builder additionalType (GraphQLNamedType additionalType ) {
731835 this .additionalTypes .add (additionalType );
732836 return this ;
733837 }
734838
839+ /**
840+ * Clears all additional types that have been added to this builder.
841+ * <p>
842+ * This is useful when transforming an existing schema and you want to
843+ * rebuild the additional types set from scratch.
844+ *
845+ * @return this builder
846+ *
847+ * @see #additionalType(GraphQLNamedType)
848+ * @see #additionalTypes(Set)
849+ */
735850 public Builder clearAdditionalTypes () {
736851 this .additionalTypes .clear ();
737852 return this ;
0 commit comments