skip to content

Why does Spring for GraphQL favor schema-first over code-first, and what are the trade-offs and customization hooks?

level: seniorimportance: nice to knowfreq 35%

answer

  1. SDL = governed, diffable contract
  2. no compile-time link -> drift risk
  3. schema-mapping inspection report catches drift
  4. hooks: controllers < RuntimeWiringConfigurer < GraphQlSourceBuilderCustomizer
  5. code-first = schema from code (DGS)

basics

~20 s

Schema-first keeps the SDL contract as the single, human-readable, version-controlled source of truth, decoupled from Java. Trade-off: you hand-write and hand-sync SDL with resolvers; there's no compile-time link, so a schema inspection report catches mismatches.

solid answer

~50 s

Spring for GraphQL is deliberately schema-first: the SDL is the contract, reviewable and diffable in Git independent of implementation, and readable by front-end teams and tooling. Benefits: the API shape is designed and governed as text, breaking-change detection and linting run on SDL, and the same schema drives docs and clients. Cost: SDL and Java resolvers are separate artifacts with no compiler enforcing they agree — you can add a field in SDL and forget the resolver, or vice versa. Spring mitigates this with a schema-mapping inspection report at startup that flags schema fields lacking a resolver and controller methods that map to nothing. Customization is layered: annotated controllers for fetching, RuntimeWiringConfigurer for scalars/type-resolvers, and GraphQlSourceBuilderCustomizer for schema-level concerns (extra resources, field visibility, transforms). Code-first (e.g. DGS-style code generation) trades that SDL-as-truth for tighter type coupling.

go deeper

for a junior

Know schema-first means SDL is the source of truth vs generated from code.

for a middle

State a couple of pros (contract/docs) and the sync/drift cost.

for a senior

Discuss the inspection report and the layered customization hooks; compare to code-first/DGS.

for a principal

Frame API governance: schema registries, breaking-change gates in CI, contract ownership across teams, and when code-first is the better organizational fit.

**The two philosophies.** - **Schema-first:** author SDL by hand; it is the contract. Implementation (resolvers) is written separately to satisfy it. Spring for GraphQL's design centers here. - **Code-first:** derive the schema from code (annotations/builders). The Netflix DGS framework and GraphQL-Java's programmatic builders are examples; some teams also *generate* Java types from SDL (a hybrid). Spring for GraphQL can technically consume a programmatically-built schema, but the ergonomic, documented path is schema-first SDL. **Why Spring favors schema-first.** 1. **Contract-as-artifact.** SDL is plain text in `resources/graphql/`. It diffs cleanly in PRs, can be linted, and breaking-change tools (e.g. schema registries) can compare versions. The contract is legible to non-Java consumers. 2. **Design-up-front / API-first.** Front-end and back-end can agree on SDL before implementation; mocking tools consume SDL directly. 3. **Separation of concerns.** The transport/API shape is decoupled from persistence types — you are not tempted to leak JPA entities into the API just because they're convenient to annotate. 4. **Documentation.** SDL with descriptions is the API doc; GraphiQL introspects it. **Trade-offs / costs.** - **No compile-time coupling.** Nothing forces a `@SchemaMapping` method to match an SDL field or a DTO to match a type. Drift is possible. - **Manual sync.** Adding a field means editing SDL *and* providing a resolver/property. - **More ceremony** than annotating a single class in a pure code-first framework. **How Spring reduces the risk — schema-mapping inspection.** Spring for GraphQL performs a **schema-mapping inspection** on startup, comparing the schema against registered controller mappings and DTO properties. Spring Boot logs a report listing: schema fields with **no** data fetcher and not backed by a property (potential nulls / unimplemented), and controller mappings that **don't** correspond to any schema field (dead/typo'd mappings). Reviewing this report is the standard guard against SDL/Java drift. **Customization hooks, layered from field to schema.** - **Annotated controllers** (`@QueryMapping`, `@MutationMapping`, `@SubscriptionMapping`, `@SchemaMapping`, `@BatchMapping`) — everyday data fetching. - **`RuntimeWiringConfigurer`** — custom scalars, `TypeResolver`s, directive wiring. - **`GraphQlSourceBuilderCustomizer`** — operate on the `GraphQlSource.SchemaResourceBuilder`: add schema resources programmatically, set GraphQL-Java field visibility, apply `graphql.schema.idl.SchemaGeneratorPostProcessing`/schema transformations, configure the `RuntimeWiring` factory, or install instrumentation. - Beyond that, `Instrumentation` beans (tracing, metrics), `DataFetcherExceptionResolver` for error mapping, and `WebGraphQlInterceptor` for the transport layer. **When to choose which.** Prefer schema-first (the default) for public/shared APIs, multi-team contracts, or where the schema is governed. Consider code-first/generation when the team wants one source of truth in code and strong type coupling, and the schema is an implementation detail rather than a negotiated contract. **Gotcha.** Schema-first does *not* mean 'no Java types' — you still write DTOs; they just aren't the schema's source of truth. And 'schema-first' is orthogonal to codegen: you can be schema-first and still *generate* Java from SDL for type safety.

  • How do you detect that an SDL field has no backing resolver before it ships?
    Rely on Spring for GraphQL's schema-mapping inspection report, logged at startup, which lists schema fields with no data fetcher/property and controller mappings that match no field.
  • Is schema-first incompatible with generated Java types?
    No. You can stay schema-first (SDL is the truth) and still run codegen from SDL to get typed DTOs/inputs — a common hybrid for type safety without ceding the contract to code.

saying these in an interview costs you the question

  • Claiming schema-first means you write no Java types at all.
  • Believing the compiler enforces SDL/resolver agreement.
  • Equating schema-first with 'less powerful' — the layered hooks give full control.

context