Start Learning
Javaneer
Back to stage
Module 11·GraphQL with Spring

Schema-First Design

GraphQL starts with a typed schema (SDL): object types, queries, and the contract that both client and server share.

14 min readIntermediate
On this page

GraphQL starts with a schema - a typed contract, written in the Schema Definition Language (SDL), that declares every type, every field, and every entry point into your graph. Both client and server share it: the client knows exactly what it can ask for, the server knows exactly what it must resolve. Spring for GraphQL is schema-first - you write the .graphqls file, and your Java code fulfils it.

The SDL: types and fields

A schema describes the shape of your data with object types and typed fields. Here's BookVault's catalog:

type Book {
  id: ID!               # ! means non-nullable
  title: String!
  isbn: String
  availableCopies: Int!
  author: Author!       # a field can be another object type
}

type Author {
  id: ID!
  name: String!
  books: [Book!]!       # a list of Books
}

Read the type modifiers carefully - they are the contract:

  • String! - a non-null String; the server promises it's never null.
  • [Book!]! - a non-null list of non-null Books (the list exists and contains no null elements).
  • author: Author! - the graph's edges: from a Book you can traverse to its Author, and from an Author back to its books. This traversal is what lets one query gather related data.

Entry points: Query, Mutation, Subscription

Three special root types are the doors into the graph. Query is for reads:

type Query {
  book(id: ID!): Book            # fetch one book by id
  books(topic: String): [Book!]! # list books, optionally filtered
}

type Mutation {
  borrowBook(bookId: ID!, memberId: ID!): Loan   # writes (later lesson)
}

Every client query must start at a field of Query (or Mutation for writes). From that entry point, the client traverses the graph via the object fields you declared. In Spring, you drop this file in src/main/resources/graphql/schema.graphqls and Spring Boot's GraphQL starter picks it up automatically.

Schema-first vs. code-first

Spring for GraphQL is schema-first: the SDL file is the source of truth and your resolvers are validated against it at startup - a field with no data source is caught early. Some other stacks are code-first (annotations generate the schema). Schema-first keeps the contract human-readable and front-end-friendly, which is usually the point of adopting GraphQL in the first place.

Nullability is the contract

That ! is not decoration - it's a promise the engine enforces. If a non-null field (title: String!) resolves to null at runtime, GraphQL doesn't silently return null; it errors that field and propagates the null up to the nearest nullable parent. So nullability is a real design decision:

  • Mark a field ! only when it genuinely always has a value.
  • Make fields that can be absent (an optional isbn) nullable, or a single missing value can blank out a whole object.
A restaurant menu with allergen symbols

The schema is the printed menu every diner and every cook shares. Object types are the dishes; fields are the components listed under each dish; the edges (author: Author!) are the 'comes with' lines that let you follow one dish to its side. The Query type is the 'you may order from this section' header - you can't order something that isn't a starting point on the menu. And the ! is the kitchen's guarantee that a listed component is always included - promise it on something that sometimes runs out, and one missing item spoils the whole plate.

Model a review into the schema

BookVault wants to add reviews. A Book should expose its list of reviews; each Review has a rating (1-5, always present), an optional text comment, and the member who wrote it. Sketch the SDL Review type and the field you'd add to Book. Which fields get ! and which don't?

In GraphQL SDL, what does the '!' in 'title: String!' mean?

Key takeaways

  • GraphQL is schema-first in Spring: an SDL (.graphqls) file is the shared, typed contract between client and server.
  • Object types have typed fields; fields that reference other types are the graph's edges that let one query traverse related data.
  • Root types Query (reads), Mutation (writes), and Subscription (streams) are the only entry points; every query starts at one of their fields.
  • The '!' marks non-nullable fields and is enforced - a non-null field resolving to null errors and propagates up, so nullability is a deliberate design choice.
  • Spring Boot auto-loads schema.graphqls from resources/graphql and validates your resolvers against it at startup.
Was this lesson helpful?
Edit this page on GitHub