Skip to content

Dynamic schemas

We strongly recommend primarily using schema-first development. Most DGSs have a schema file and use the declarative, annotation-based programming model to create data fetchers and such. That said, there are scenarios where generating the schema from another source, possibly dynamically, is required.

Creating a schema from code

Create a schema from code by using the @DgsTypeDefinitionRegistry annotation. Use the @DgsTypeDefinitionRegistry on methods inside a @DgsComponent class to provide a TypeDefinitionRegistry.

The TypeDefinitionRegistry is part of the graphql-java API. You use a TypeDefinitionRegistry to programmatically define a schema.

Note that you can mix static schema files with one or more DgsTypeDefinitionRegistry methods. The result is a schema with all the registered types merged. This way, you can primarily use a schema-first workflow while falling back to @DgsTypeDefinitionRegistry to add some dynamic parts to the schema.

The following is an example of a DgsTypeDefinitionRegistry.

@DgsComponent
public class DynamicTypeDefinitions {
    @DgsTypeDefinitionRegistry
    public TypeDefinitionRegistry registry() {
        TypeDefinitionRegistry typeDefinitionRegistry = new TypeDefinitionRegistry();

        ObjectTypeExtensionDefinition query = ObjectTypeExtensionDefinition.newObjectTypeExtensionDefinition().name("Query").fieldDefinition(
                FieldDefinition.newFieldDefinition().name("randomNumber").type(new TypeName("Int")).build()
        ).build();

        typeDefinitionRegistry.add(query);

        return typeDefinitionRegistry;
    }
}

This TypeDefinitionRegistry creates a field randomNumber on the Query object type.

Creating datafetchers programmatically

If you're creating schema elements dynamically, it's likely you also need to create datafetchers dynamically. You can use the @DgsCodeRegistry annotation to add datafetchers programmatically. A method annotated @DgsCodeRegistry takes two arguments:

GraphQLCodeRegistry.Builder codeRegistryBuilder TypeDefinitionRegistry registry

The method must return the modified GraphQLCodeRegistry.Builder.

The following is an example of a programmatically created datafetcher for the field created in the previous example.

@DgsComponent
public class DynamicDataFetcher {
@DgsCodeRegistry
public GraphQLCodeRegistry.Builder registry(GraphQLCodeRegistry.Builder codeRegistryBuilder, TypeDefinitionRegistry registry) {
DataFetcher<Integer> df = (dfe) -> new Random().nextInt();
FieldCoordinates coordinates = FieldCoordinates.coordinates("Query", "randomNumber");

        return codeRegistryBuilder.dataFetcher(coordinates, df);
    }
}

Changing schemas at runtime

It's helpful to combine creating schemas/datafetchers at runtime with dynamically re-loading the schema in some very rare use-cases.
You can achieve this by implementing your own ReloadSchemaIndicator. You can use an external signal (e.g., reading from a message queue) to have the framework recreate the schema by executing the @DgsTypeDefinitionRegistry and @DgsCodeRegistry again. If these methods create the schema based on external input, you have a system that can dynamically rewire its API.

For obvious reasons, this isn't an approach that you should use for typical APIs; stable APIs are generally the thing to aim for!