Schema and type system
The schema definition language, nullability, and why the schema is the contract your clients are generated from.
Schema definition language
A GraphQL service is described by one schema. Clients can introspect it, tools generate types from it, and the server validates every incoming document against it before executing anything.
type Query {
post(id: ID!): Post
posts(first: Int = 10, after: String): [Post!]!
}
type Post {
id: ID!
title: String!
body: String!
publishedAt: DateTime
author: User!
comments(first: Int = 5): [Comment!]!
}
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Comment {
id: ID!
body: String!
author: User!
}
scalar DateTime| Type | Use |
|---|---|
Int, Float, String, Boolean | The built-in scalars |
ID | An opaque identifier, serialised as a string |
[Post!]! | A non-null list of non-null posts: two independent guarantees |
enum | A fixed set of allowed values, validated by the server |
interface | Shared fields implemented by several object types |
union | One of several types that share no fields |
input | A structured argument type, used mainly by mutations |
Queries, mutations and the type system
query PostPage($id: ID!, $withComments: Boolean! = false) {
post(id: $id) {
id
title
author { name }
comments(first: 3) @include(if: $withComments) {
body
author { name }
}
}
}
mutation CreatePost($input: CreatePostInput!) {
createPost(input: $input) {
post { id title }
errors { field message }
}
}- The client sends a document; the server parses and validates it against the schema, so a typo returns an error with a path instead of failing at runtime.
- An exclamation mark marks a field as non-null. Nullability is part of the contract and is what drives client type generation.
- Fragments keep a repeated selection in one place, and inline fragments let you select fields on an interface or union member.
@includeand@skipmake part of a shape conditional, so one document serves several screens.- Variables are typed and validated too, which also keeps user input out of the document text.
💡
Design the schema around what clients ask for, not around your database tables. Exposing rows one-to-one couples every client to your storage model, and any later refactor becomes a breaking change.
FAQ
What does N+1 mean in GraphQL?
One resolver loads a list, then a child resolver runs once per item and issues a query each time. The client sent a single query, but the server performed one plus N database queries.
Do I version a GraphQL schema?
Prefer evolution. Add fields, mark old ones deprecated with a reason, and remove them only after clients stop selecting them. Versioning the endpoint is a last resort.
Related
Queries, mutations and resolvers When GraphQL beats REST, and its pitfalls
Last refreshed 2026-09-18.