FastSchemaGenerator.java
package graphql.schema.idl;
import graphql.ExperimentalApi;
import graphql.GraphQLError;
import graphql.language.OperationTypeDefinition;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLDirective;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.errors.SchemaProblem;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static graphql.schema.idl.SchemaGeneratorHelper.buildDescription;
/**
* A schema generator that uses {@link GraphQLSchema.FastBuilder} to construct the schema.
* {@link GraphQLSchema.FastBuilder} has a number of important limitations, so please read
* its documentation carefully to understand if you should use this instead of the standard
* {@link SchemaGenerator}.
*
* @see GraphQLSchema.FastBuilder
* @see SchemaGenerator
*/
@ExperimentalApi
@NullMarked
public class FastSchemaGenerator {
private final SchemaTypeChecker typeChecker = new SchemaTypeChecker();
private final SchemaGeneratorHelper schemaGeneratorHelper = new SchemaGeneratorHelper();
/**
* Creates an executable schema from a TypeDefinitionRegistry using FastBuilder.
*
* @param typeRegistry the type definition registry
* @param wiring the runtime wiring
* @return a validated, executable schema
*/
public GraphQLSchema makeExecutableSchema(TypeDefinitionRegistry typeRegistry, RuntimeWiring wiring) {
return makeExecutableSchema(SchemaGenerator.Options.defaultOptions(), typeRegistry, wiring);
}
/**
* Creates an executable schema from a TypeDefinitionRegistry using FastBuilder.
*
* @param options the schema generation options
* @param typeRegistry the type definition registry
* @param wiring the runtime wiring
* @return an executable schema
*/
public GraphQLSchema makeExecutableSchema(SchemaGenerator.Options options,
TypeDefinitionRegistry typeRegistry,
RuntimeWiring wiring) {
// Make a copy and add default directives
TypeDefinitionRegistry typeRegistryCopy = new TypeDefinitionRegistry();
typeRegistryCopy.merge(typeRegistry);
schemaGeneratorHelper.addDirectivesIncludedByDefault(typeRegistryCopy);
// Use immutable registry for faster operations
ImmutableTypeDefinitionRegistry fasterImmutableRegistry = typeRegistryCopy.readOnly();
// Check type registry for errors
List<GraphQLError> errors = typeChecker.checkTypeRegistry(fasterImmutableRegistry, wiring);
if (!errors.isEmpty()) {
throw new SchemaProblem(errors);
}
Map<String, OperationTypeDefinition> operationTypeDefinitions = SchemaExtensionsChecker.gatherOperationDefs(fasterImmutableRegistry);
return makeExecutableSchemaImpl(fasterImmutableRegistry, wiring, operationTypeDefinitions, options);
}
private GraphQLSchema makeExecutableSchemaImpl(ImmutableTypeDefinitionRegistry typeRegistry,
RuntimeWiring wiring,
Map<String, OperationTypeDefinition> operationTypeDefinitions,
SchemaGenerator.Options options) {
// Build all types using the standard helper
SchemaGeneratorHelper.BuildContext buildCtx = new SchemaGeneratorHelper.BuildContext(
typeRegistry, wiring, operationTypeDefinitions, options);
// Build directives
Set<GraphQLDirective> additionalDirectives = schemaGeneratorHelper.buildAdditionalDirectiveDefinitions(buildCtx);
// Use a dummy builder to trigger type building (this populates buildCtx)
GraphQLSchema.Builder tempBuilder = GraphQLSchema.newSchema();
schemaGeneratorHelper.buildOperations(buildCtx, tempBuilder);
// Build all additional types
Set<GraphQLNamedType> additionalTypes = schemaGeneratorHelper.buildAdditionalTypes(buildCtx);
// Set field visibility on code registry
buildCtx.getCodeRegistry().fieldVisibility(buildCtx.getWiring().getFieldVisibility());
// Build the code registry
GraphQLCodeRegistry codeRegistry = buildCtx.getCodeRegistry().build();
// Extract operation types by name from built types (all types from buildCtx are named types)
Set<GraphQLNamedType> allBuiltTypes = buildCtx.getTypes().stream()
.map(t -> (GraphQLNamedType) t)
.collect(Collectors.toSet());
// Get the actual type names from operationTypeDefinitions, defaulting to standard names
String queryTypeName = getOperationTypeName(operationTypeDefinitions, "query", "Query");
String mutationTypeName = getOperationTypeName(operationTypeDefinitions, "mutation", "Mutation");
String subscriptionTypeName = getOperationTypeName(operationTypeDefinitions, "subscription", "Subscription");
GraphQLObjectType queryType = findOperationType(allBuiltTypes, queryTypeName);
GraphQLObjectType mutationType = findOperationType(allBuiltTypes, mutationTypeName);
GraphQLObjectType subscriptionType = findOperationType(allBuiltTypes, subscriptionTypeName);
if (queryType == null) {
throw new IllegalStateException("Query type '" + queryTypeName + "' is required but was not found");
}
// Create FastBuilder
GraphQLSchema.FastBuilder fastBuilder = new GraphQLSchema.FastBuilder(
GraphQLCodeRegistry.newCodeRegistry(codeRegistry),
queryType,
mutationType,
subscriptionType);
// Add all built types
fastBuilder.addTypes(allBuiltTypes);
fastBuilder.addTypes(additionalTypes);
// Add all directive definitions
fastBuilder.additionalDirectives(additionalDirectives);
// Add schema description and definition if present
typeRegistry.schemaDefinition().ifPresent(schemaDefinition -> {
String description = buildDescription(buildCtx, schemaDefinition, schemaDefinition.getDescription());
fastBuilder.description(description);
fastBuilder.definition(schemaDefinition);
});
// Configure validation
fastBuilder.withValidation(options.isWithValidation());
return fastBuilder.build();
}
private String getOperationTypeName(Map<String, OperationTypeDefinition> operationTypeDefs,
String operationName,
String defaultTypeName) {
OperationTypeDefinition opDef = operationTypeDefs.get(operationName);
if (opDef != null) {
return opDef.getTypeName().getName();
}
return defaultTypeName;
}
private @Nullable GraphQLObjectType findOperationType(Set<GraphQLNamedType> types, String typeName) {
for (GraphQLNamedType type : types) {
if (type instanceof GraphQLObjectType) {
GraphQLObjectType objectType = (GraphQLObjectType) type;
if (objectType.getName().equals(typeName)) {
return objectType;
}
}
}
return null;
}
}