What is RuntimeWiringConfigurer and when do you need one instead of just annotated controllers?
answer
- configure(RuntimeWiring.Builder)
- custom scalars — no annotation exists
- TypeResolver for interface/union
- directive wiring (SchemaDirectiveWiring)
- AnnotatedControllerConfigurer is one too
basics
~20 sRuntimeWiringConfigurer is a bean that customizes how the schema is wired at build time. You add one to register custom scalar types, TypeResolvers for interfaces/unions, or directive wiring — things annotated @QueryMapping controllers can't express.
solid answer
~40 sRuntimeWiringConfigurer is a functional interface with configure(RuntimeWiring.Builder). Every bean of this type is invoked while the GraphQlSource is being built, letting you contribute to the RuntimeWiring — the field-to-DataFetcher and type mappings. Annotated controllers cover most fetching, but they can't declare custom scalars, resolve abstract types, or wire directives, so you drop to a RuntimeWiringConfigurer for those. The three common cases: (1) custom scalars — register a graphql.schema.GraphQLScalarType, e.g. the extended-scalars DateTime or a domain scalar, so SDL scalar declarations become usable; (2) TypeResolver — for GraphQL interface or union types, tell GraphQL which concrete object type a returned instance maps to; (3) directive wiring via SchemaDirectiveWiring. Spring's own AnnotatedControllerConfigurer is itself a RuntimeWiringConfigurer, so your beans compose alongside it.
code
java · 23 lines// schema.graphqls:
// scalar DateTime
// type Query { search(term: String!): [SearchResult!]! }
// union SearchResult = Book | Author
// type Book { id: ID! title: String! publishedAt: DateTime }
// type Author { id: ID! name: String! }
@Configuration
class GraphQlWiring {
@Bean
RuntimeWiringConfigurer runtimeWiringConfigurer() {
return wiring -> wiring
// 1) custom scalar (from graphql-java-extended-scalars)
.scalar(ExtendedScalars.DateTime)
// 2) TypeResolver so a union member resolves to its object type
.type("SearchResult", builder -> builder.typeResolver(env -> {
Object o = env.getObject();
String name = (o instanceof Book) ? "Book" : "Author";
return env.getSchema().getObjectType(name);
}));
}
}go deeper
Aware that some customization (custom scalars) isn't done by controllers.
Know RuntimeWiringConfigurer.configure(builder) exists and registers scalars/type resolvers.
Enumerate the three cases (scalars, TypeResolver, directives) and how configurers compose with AnnotatedControllerConfigurer.
Weigh configurer ordering, separation of concerns vs controllers, and where GraphQlSourceBuilderCustomizer belongs instead.
**Recap of wiring.** SDL declares field shapes; the executable schema needs **RuntimeWiring** — the object mapping each field to a `DataFetcher`, each custom scalar to a `GraphQLScalarType`, and each interface/union to a `TypeResolver`. Spring builds this while assembling the `GraphQlSource`. **The interface.** `org.springframework.graphql.execution.RuntimeWiringConfigurer` has one method: `void configure(RuntimeWiring.Builder builder)`. Declare any number of beans; each is called with the shared `RuntimeWiring.Builder`. Spring's `AnnotatedControllerConfigurer` (which registers your `@QueryMapping`/`@SchemaMapping` fetchers) is *also* a `RuntimeWiringConfigurer`, so your custom configurers and the annotated-controller wiring merge into the same `RuntimeWiring`. **When annotated controllers are NOT enough — the three canonical cases.** 1. **Custom scalars.** GraphQL has only a few built-in scalars (`Int`, `Float`, `String`, `Boolean`, `ID`). To use `DateTime`, `Long`, `BigDecimal`, `Url`, etc., you (a) declare `scalar DateTime` in SDL and (b) register a `GraphQLScalarType` telling GraphQL how to serialize/parse it. The `graphql-java-extended-scalars` library provides ready ones (`ExtendedScalars.DateTime`, `.GraphQLLong`, `.Json`…). You register them only via a `RuntimeWiringConfigurer` — there is no annotation for scalars. ```java @Bean public RuntimeWiringConfigurer scalars() { return wiring -> wiring .scalar(ExtendedScalars.DateTime) .scalar(ExtendedScalars.GraphQLLong); } ``` 2. **`TypeResolver` for interfaces and unions.** If SDL has `interface Animal` implemented by `Dog`/`Cat`, or `union SearchResult = Book | Author`, GraphQL must decide, for a returned object, which concrete type to use to select fields. That decision is a `graphql.schema.TypeResolver`. Register it with `.type("Animal", tw -> tw.typeResolver(env -> ...))`. (Spring can infer by class name in some cases, but explicit resolvers are common.) 3. **Directive wiring.** To implement custom schema directives (e.g. `@uppercase`, `@auth`) you register a `SchemaDirectiveWiring` via the builder to transform field fetchers. **Other uses.** Registering a global/fallback `DataFetcher`, or plugging in a fetching mechanism that isn't a controller. **Gotchas.** - A `scalar Foo` in SDL with no registered `GraphQLScalarType` fails schema assembly at startup. - Order: all `RuntimeWiringConfigurer` beans run against the same builder; there is no guaranteed cross-bean ordering unless you make them `Ordered`. Avoid two configurers clobbering the same field. - Prefer annotated controllers for plain fetching; reserve `RuntimeWiringConfigurer` for scalars/type-resolution/directives. Mixing everything into one giant configurer loses the clarity of controller-based fetching. - Don't confuse it with `GraphQlSourceBuilderCustomizer`, which operates one level up (on the `SchemaResourceBuilder`, e.g. to add schema files programmatically, set field visibility, or apply schema transforms) rather than on the `RuntimeWiring`.
- You declared `scalar DateTime` in SDL but the app fails to start. Why?A custom scalar needs a registered GraphQLScalarType via a RuntimeWiringConfigurer (e.g. ExtendedScalars.DateTime). Without it, schema assembly can't resolve the scalar and startup fails.
- How does RuntimeWiringConfigurer differ from GraphQlSourceBuilderCustomizer?RuntimeWiringConfigurer edits the RuntimeWiring (field fetchers, scalars, type resolvers). GraphQlSourceBuilderCustomizer works on the SchemaResourceBuilder one level up — adding schema resources, field visibility, schema transforms, or setting the RuntimeWiring wholesale.
saying these in an interview costs you the question
- Saying you register custom scalars with an annotation — there is no scalar annotation; it must be a RuntimeWiringConfigurer.
- Confusing RuntimeWiringConfigurer with GraphQlSourceBuilderCustomizer.
- Assuming interface/union types resolve automatically without a TypeResolver in all cases.