Working with GraphQL Schema
1. Creating Schema Definition
| Element | Purpose | Example |
|---|---|---|
| schema | Declares root operation types | schema { query: Query } |
| type | Object output type | type User { id: ID! } |
| input | Argument type | input UserInput { ... } |
| enum / interface / union / scalar | Other type kinds | — |
Example: Minimal SDL
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type Query {
hello: String!
user(id: ID!): User
}
type User {
id: ID!
name: String!
email: String!
}
2. Defining Root Query Type
| Rule | Detail |
|---|---|
| Required | Every schema must have a Query type |
| Read-only | Conventionally side-effect free, parallelizable |
| Naming | Use noun field names: users, user(id) |
Example: Query root
type Query {
me: User
users(first: Int = 20, after: String): UserConnection!
product(id: ID!): Product
}
3. Defining Root Mutation Type
| Rule | Detail |
|---|---|
| Optional | Only required if writes are exposed |
| Sequential | Top-level fields execute serially (spec) |
| Naming | Verb-noun: createUser, updatePost |
| Payload | Return a payload type for extensibility |
Example: Mutation with payload
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
}
type CreateUserPayload {
user: User
errors: [UserError!]!
}
4. Defining Root Subscription Type
| Rule | Detail |
|---|---|
| Transport | WebSocket (graphql-ws) or SSE |
| Single field | Operation must select exactly one root field |
| Resolver | Returns AsyncIterator of events |
5. Using Schema Description Strings
| Syntax | Use |
|---|---|
"single line" | Short description above type/field |
"""block""" | Multi-line, supports markdown |
| Available on | types, fields, args, enum values, directives |
Example: Descriptions
"""A registered customer of the platform."""
type User {
"Globally unique identifier"
id: ID!
"""
Display name. **May be null** for anonymous users.
"""
name: String
}
6. Organizing Schema Files
| Strategy | When |
|---|---|
| Single file | Small APIs (< 200 lines) |
| Per type | Medium APIs; one .graphql per domain type |
| Per domain module | Large APIs; users/, orders/ folders with schema + resolvers |
| Federation | Schema split across services |
Example: Loading multiple SDL files
import { loadFilesSync } from "@graphql-tools/load-files";
import { mergeTypeDefs } from "@graphql-tools/merge";
const typeDefs = mergeTypeDefs(
loadFilesSync("./src/**/*.graphql")
);
7. Extending Schema Definitions
| Keyword | Use |
|---|---|
| extend type | Add fields to existing object |
| extend interface / enum / input | Add members |
| extend schema | Add directives or root types |
Example: Module extends Query
# users/schema.graphql
type Query
extend type Query {
me: User
user(id: ID!): User
}
8. Naming Conventions for Types
| Element | Convention | Example |
|---|---|---|
| Types/Interfaces/Unions | PascalCase, singular | User, Node |
| Input types | Suffix Input | CreateUserInput |
| Payload types | Suffix Payload | LoginPayload |
| Connections/Edges | Suffix per Relay spec | UserConnection, UserEdge |
| Enums | PascalCase singular | OrderStatus |
9. Naming Conventions for Fields
| Element | Convention | Example |
|---|---|---|
| Fields/Args | camelCase | firstName, orderBy |
| Booleans | is/has prefix | isActive, hasAccess |
| Lists | plural noun | posts, tags |
| Mutations | verbNoun | createPost, archiveOrder |
| Enum values | SCREAMING_SNAKE_CASE | IN_PROGRESS |
10. Using Comments in Schema
| Syntax | Visibility | Use |
|---|---|---|
# comment | Source-only, not in introspection | Implementer notes |
"description" | Exposed via introspection | API docs for clients |
Warning: Use
"""...""" for anything clients should see. # comments are stripped from introspection.11. Validating Schema Design
| Tool | Checks |
|---|---|
| graphql-eslint | Naming, deprecation, anti-patterns |
| GraphQL Inspector | Breaking change diff vs prior schema |
| apollo schema check | Validates against operation traffic |
| graphql-schema-linter | Style, descriptions, types-have-descriptions |