GraphQL Advanced Vocabulary: SDL, Resolvers, Federation, DataLoader, and More
Master advanced GraphQL vocabulary: Schema Definition Language, resolvers, mutations, subscriptions, the N+1 problem, DataLoader, schema federation, Apollo Federation, persisted queries, and API gateway patterns. For engineers building and consuming GraphQL APIs.
GraphQL has its own precise vocabulary that differs significantly from REST. If you join a team that uses GraphQL — or build one of the thousands of public and internal GraphQL APIs — you need to know this language.
This guide covers the 40 advanced GraphQL terms that matter most, from Schema Definition Language to federation architecture to API gateway patterns.
What Is GraphQL?
GraphQL is a query language for APIs and a runtime for fulfilling those queries. Unlike REST (which has fixed endpoints that return fixed data shapes), GraphQL lets clients specify exactly what data they need, request multiple resources in a single query, and update data via mutations.
GraphQL was created by Facebook / Meta in 2012 and open-sourced in 2015.
Schema Definition Language (SDL)
SDL (Schema Definition Language)
The Schema Definition Language (SDL) is the language used to define a GraphQL schema. It describes the types, fields, queries, mutations, and subscriptions the API exposes.
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Query {
user(id: ID!): User
users: [User!]!
}
“Before writing any resolver logic, we start with the SDL — schema-first design means frontend and backend teams can agree on the contract before implementation begins.”
Type System
GraphQL’s type system is strongly typed. Every field has a declared type. Types include:
- Scalar types:
String,Int,Float,Boolean,ID - Object types: user-defined types with fields (e.g.,
User,Post) - Input types: types used for mutation arguments (e.g.,
CreateUserInput) - Enum types: a fixed set of allowed values
- Interface types: abstract types that object types can implement
- Union types: a field that can return one of several types
Non-Null (!)
The exclamation mark (!) in GraphQL SDL means a field is non-null — it will always return a value, never null.
name: String! — always a string
name: String — may be null
“We made
Input Type
An input type is a special object type used for mutation arguments. Unlike regular object types, input types cannot have resolvers — they are pure data structures.
input CreateUserInput {
name: String!
email: String!
role: UserRole!
}
Enum
A GraphQL enum defines a field that can only be one of a fixed set of values.
enum UserRole {
ADMIN
EDITOR
VIEWER
}
Operations: Query, Mutation, Subscription
Query
A query is a read operation in GraphQL — retrieving data. Queries are the GraphQL equivalent of HTTP GET requests.
query {
user(id: "123") {
name
email
posts {
title
}
}
}
Mutation
A mutation is a write operation — creating, updating, or deleting data. Mutations are the equivalent of POST, PUT, PATCH, DELETE in REST.
mutation {
createUser(input: { name: "Alice", email: "alice@example.com" }) {
id
name
}
}
“All state changes go through mutations. We always return the modified entity from a mutation so the client can update its cache without a separate query.”
Subscription
A subscription is a real-time operation — the client subscribes to a data stream and receives updates when the data changes. Implemented over WebSockets or server-sent events.
subscription {
messageAdded(channelId: "general") {
id
content
author {
name
}
}
}
Operation Name
An operation name is an optional identifier given to a query, mutation, or subscription. Strongly recommended in production for debugging, logging, and tracing.
query GetUserProfile {
user(id: "123") { name }
}
Fragment
A fragment is a reusable unit of fields that can be embedded in multiple operations. Reduces repetition.
fragment UserFields on User {
id
name
email
}
query {
user(id: "1") { ...UserFields }
}
Resolvers
Resolver
A resolver is a function that populates the data for a single field in the schema. Every field can have a resolver; fields without explicit resolvers use a default resolver that reads from the parent object.
const resolvers = {
Query: {
user: (parent, args, context, info) => {
return context.db.findUserById(args.id);
}
}
};
Resolver Arguments
Resolvers receive four arguments:
- parent (or
root): the result of the parent resolver - args: the arguments passed to the field (e.g.,
id: "123") - context: shared request context — authentication info, database connections, loaders
- info: metadata about the current execution (field name, path, schema)
Resolver Chain
When a query is executed, resolvers form a chain: the root resolver runs first, then each child field’s resolver runs in turn. A child resolver receives the parent’s result as its first argument.
“The
postsfield onUserhas its own resolver — it receives the user object as the parent and fetches posts for that specific user.”
The N+1 Problem
N+1 Problem
The N+1 problem is a classic GraphQL (and ORM) performance issue: if you fetch a list of N items and each item’s field requires a separate database query, you end up with 1 query to fetch the list + N queries for the field — N+1 total.
Example:
query {
users { # 1 query: fetch all users
name
posts { # N queries: one per user to fetch their posts
title
}
}
}
For 100 users, this is 101 database queries.
DataLoader
DataLoader is a utility library that solves the N+1 problem by batching and caching requests. Instead of running a query per user, DataLoader collects all user IDs requested in a single tick and runs one batched query.
const userLoader = new DataLoader(async (userIds) => {
const users = await db.getUsersByIds(userIds); // one query
return userIds.map(id => users.find(u => u.id === id));
});
“Before DataLoader, fetching 100 users with their posts made 101 queries. After DataLoader, it makes 2 — one for users, one for all their posts combined.”
Batching
Batching (in DataLoader) is collecting multiple individual requests and executing them as a single operation in the same tick of the event loop.
Caching (DataLoader)
DataLoader also provides per-request caching — if the same entity is requested twice in the same request, it is only fetched once and the result is cached for the duration of the request. The cache should be cleared between requests.
Schema Federation
Schema Federation
Schema federation is an architecture for composing a single, unified GraphQL schema from multiple independent subgraph services. Each team owns a subgraph; a gateway stitches them together into a supergraph.
“Each team owns their GraphQL subgraph — the user team owns user types, the product team owns product types. Our gateway federates them into one API that clients query.”
Supergraph
The supergraph is the complete, composed GraphQL schema — the unified result of combining all subgraphs. Clients query the supergraph without knowing which subgraph owns which data.
Subgraph
A subgraph is one service’s portion of the federated schema. A subgraph defines its own types and the resolvers to fulfill them, plus @key directives to allow other subgraphs to reference its entities.
Apollo Federation
Apollo Federation is the most widely-used schema federation specification, developed by Apollo GraphQL. It defines the directives (@key, @external, @requires, @provides) and the router/gateway architecture.
@key Directive
The @key directive marks an entity’s unique identifier in Apollo Federation — allowing other subgraphs to reference and resolve that entity.
type User @key(fields: "id") {
id: ID!
name: String!
}
Schema Stitching
Schema stitching is an older (and less recommended) approach to combining multiple GraphQL APIs into one, via runtime merging. Federation is generally preferred for new projects.
Gateway / Router
The gateway (or router in Apollo’s terminology) is the entry point that receives all client queries, plans the execution across subgraphs, and assembles the final response.
Performance & Operations
Persisted Queries
Persisted queries (or automatic persisted queries / APQ) are a technique where the client sends a hash of the query instead of the full query text. The server looks up the full query by hash, reducing bandwidth and preventing arbitrary query execution.
“We use persisted queries in production — clients send the query hash, not the full SDL. This also lets us block queries that aren’t in our allowlist.”
Introspection
Introspection is GraphQL’s built-in ability to query the schema itself — asking “what types and fields does this API have?” Introspection powers GraphQL tooling (explorers, code generators, IDEs).
“We disable introspection in production — attackers could use it to understand our entire data model. Introspection is enabled in staging.”
Query Depth Limiting
Query depth limiting prevents queries that are deeply nested (potentially causing expensive database joins or infinite recursion in circular schemas). A depth limit of 5–7 is common.
Query Cost Analysis
Query cost analysis assigns a numeric cost to each field and rejects queries that exceed a total cost threshold — a more sophisticated alternative to depth limiting.
Schema Drift
Schema drift occurs when the actual resolvers diverge from the declared schema — fields exist in the schema but their resolvers are broken or missing. Schema registry tooling and integration tests detect drift.
API Gateway Patterns
API Gateway
An API gateway is a reverse proxy that sits between clients and backend services. It handles cross-cutting concerns: routing, rate limiting, authentication, request transformation, response caching. GraphQL gateways are a specialised form.
Rate Limiting
Rate limiting at the gateway level restricts how many requests a client can make in a time window. For GraphQL, this is often implemented per operation or per query cost, not per HTTP request (since one HTTP request can contain multiple queries).
API Key Management
API key management at the gateway controls which clients are authorised to use the API, tracks usage per key, and enables revocation. Gateway-level API keys are separate from user authentication tokens.
Request Transformation
Request transformation at the gateway level modifies requests before forwarding to upstream services — adding headers, normalising formats, injecting context. Some gateways can transform REST responses into GraphQL.
Useful Phrases
| Situation | Phrase |
|---|---|
| Describing the N+1 fix | ”We implemented DataLoader for all entity-level field resolvers — the query count dropped from 200 to 3.” |
| Explaining federation | ”Each team publishes a subgraph that owns its domain types. The supergraph federates them automatically — clients don’t see the boundary.” |
| Discussing introspection | ”We disable introspection in production and only allow registered persisted queries to prevent schema enumeration.” |
| Schema evolution | ”Adding a new optional field to a type is backward compatible — existing clients that don’t request it are unaffected.” |
| Choosing REST vs. GraphQL | ”GraphQL is worth the complexity when clients need flexible data fetching. For simple CRUD with a single consumer, REST is simpler and better understood.” |