Vocabulary for GraphQL APIs: Schema, Resolvers, Mutations, and Subscriptions
Master core GraphQL vocabulary: schema, types, queries, mutations, subscriptions, resolvers, fragments, and the N+1 problem. Precise English for engineers building GraphQL APIs.
GraphQL has a vocabulary all its own, and it differs sharply from REST. If you describe a GraphQL API using REST words — “endpoints,” “routes,” “GET and POST” — you’ll sound out of your depth. This guide covers the core terms, the verb collocations engineers actually use, and the distinctions (query vs. mutation, field vs. type) that matter in conversation.
The mental shift: one endpoint, a typed graph
In REST, you have many endpoints, one per resource. In GraphQL, you have one endpoint and a typed schema describing everything available. The client asks for exactly the fields it needs — no more, no less.
“Instead of hitting five REST endpoints, the client sends one query to the GraphQL endpoint and asks for exactly the fields it needs in a single round trip.”
Two phrases that capture GraphQL’s selling points:
- Over-fetching — getting more data than you need (a REST problem GraphQL solves).
- Under-fetching — needing several requests to get everything (also solved).
The schema and the type system
The schema is the contract — it defines every type and operation.
- Type — a shape of data, e.g.
User,Post. - Field — a property on a type, e.g.
user.email. - Scalar — a primitive:
String,Int,Boolean,ID,Float. - Nullable vs. non-null —
Stringcan be null;String!cannot. - Enum — a fixed set of allowed values.
- Interface / union — ways to express polymorphic types.
- Input type — a type used for arguments (e.g. a mutation’s payload).
- SDL — Schema Definition Language, the syntax you write the schema in.
Collocations:
- you define a type / add a field / mark a field non-null
- the schema exposes a query
- a field resolves to a value
“We define a
Usertype with a non-nullidfield and a nullablebio. The schema exposes auser(id: ID!)query.”
Pronunciation: schema is /ˈskiːmə/ (SKEE-muh). Not “shema.” This trips up a lot of speakers.
The three operation types
GraphQL has exactly three operation types — know the difference cold:
- Query — read data. (Like GET.)
- Mutation — write/change data. (Like POST/PUT/DELETE.)
- Subscription — receive real-time updates over a persistent connection.
“We expose a
postsquery to read, acreatePostmutation to write, and apostAddedsubscription so clients get live updates.”
A common error: calling a write a “query.” In GraphQL, reads are queries, writes are mutations. If you say “I’ll write a query to update the user,” a GraphQL engineer will correct you to “mutation.”
Resolvers: where the data comes from
A resolver is the function that produces the value for a field. This is the heart of a GraphQL server.
- a resolver resolves a field
- resolvers run per field, top-down
- a resolver fetches data from a database or another service
“The
userresolver fetches the user from Postgres; theuser.postsresolver then resolves the posts. Each field has its own resolver.”
Related terms:
- Resolver chain — resolvers calling down into nested fields.
- Context — shared data (auth, loaders) passed to every resolver.
- Root resolver — the entry point (Query, Mutation, Subscription).
The N+1 problem and DataLoader
The single most-discussed GraphQL performance issue:
- N+1 problem — fetching a list of N items, then making N more queries for each item’s nested data (1 + N queries).
- Batching — combining many small fetches into one.
- DataLoader — a utility that batches and caches these fetches within a request.
“Loading 50 posts triggered 50 separate author lookups — the classic N+1 problem. We added a DataLoader to batch them into one query.”
This is a near-guaranteed interview question for GraphQL roles. Be able to define it in one sentence.
Querying: fields, arguments, fragments, variables
On the client side:
- Field selection — choosing which fields to return.
- Argument — a parameter on a field, e.g.
user(id: 5). - Variable — a
$-prefixed placeholder for dynamic values. - Fragment — a reusable set of fields.
- Alias — renaming a field in the response.
- Directive — a modifier like
@includeor@skip.
“We extract the shared fields into a fragment, pass the ID as a variable, and use a directive to conditionally include the
Errors, deprecation, and evolution
- Partial response — GraphQL can return data and errors together.
- Errors array — where field-level errors appear, alongside
data. - Deprecation — mark a field as obsolete with
@deprecated(reason: "...")rather than deleting it. - Schema evolution — adding fields is safe (additive); removing or renaming is a breaking change.
“Rather than deleting
fullName, we deprecated it with a reason and addedfirstName/lastName. Additive changes are backward-compatible; removals are breaking.”
REST → GraphQL vocabulary swap
| REST word | GraphQL word |
|---|---|
| Endpoint / route | Single endpoint + schema |
| GET | Query |
| POST / PUT / DELETE | Mutation |
| Resource | Type |
| Response field | Field (you select them) |
| Polling for updates | Subscription |
Common mistakes non-native engineers make
- Calling a write a “query.” Reads are queries; writes are mutations.
- Saying “endpoints” (plural). GraphQL has one endpoint and a schema.
- Mispronouncing “schema.” It’s SKEE-muh.
- Confusing field and type. A type is the shape (
User); a field is a property on it (email). - Saying “the API returns too much.” Use over-fetching — the precise term.
Key takeaways
- GraphQL means one endpoint, a typed schema, and clients selecting exactly the fields they need.
- The three operations are query (read), mutation (write), subscription (real-time) — never call a write a query.
- Resolvers produce field values; the famous performance trap is the N+1 problem, solved with DataLoader batching.
- Evolve schemas by deprecating, not deleting — additions are safe, removals are breaking changes.
- Drop REST words like “endpoints” and “routes”; using GraphQL’s own vocabulary is what makes you sound fluent.
Mastering GraphQL Vocabulary – Beyond the Basics
Let’s be honest; navigating the terminology of GraphQL can feel like deciphering a new language. While the core concepts are powerful, precise communication is crucial for successful API development. This isn’t just about knowing what something is; it’s about using the right phrasing to collaborate effectively with other engineers – particularly those who might not have a deep familiarity with GraphQL’s nuances. Let’s tackle some of the more subtle aspects of communicating about GraphQL, focusing on clarity and precision that are often overlooked.
One common issue arises when discussing performance, specifically the N+1 problem. It’s easy to simply state “We have an N+1 problem.” However, a more helpful approach – especially during code reviews – is to frame it as: “The current resolver implementation results in an N+1 query pattern for fetching related data. This means that for each item retrieved initially, we’re executing N additional queries to fetch associated items, leading to significant performance degradation as the dataset grows. We should investigate alternative solutions like using DataLoader or batching requests to mitigate this.” Notice the detail – it’s not just about identifying the problem, but outlining its impact and hinting at a potential solution. Similarly, when discussing schema design, avoid vague statements like “The schema is poorly designed.” Instead, aim for something like: “The current schema lacks a clear type definition for ‘orderItems’, leading to ambiguity in querying related data and potentially increasing complexity during future modifications.” Focusing on the effect of a design choice rather than simply stating it’s “bad” will lead to more productive conversations.
Furthermore, when describing resolvers – particularly those dealing with complex relationships – use precise language. Saying “the resolver needs optimization” is too general. Instead, try: “The current resolver’s query complexity is exceeding acceptable thresholds. Specifically, the nested JOIN operations are contributing to increased latency and should be revisited using a more efficient data access pattern.” This demonstrates an understanding of why optimization is needed – it’s not just about making things faster; it’s about addressing specific bottlenecks.
Finally, when reviewing someone else’s code related to subscriptions or mutations, avoid generalizations like “this could cause issues with scaling”. A more constructive approach would be: “This mutation logic doesn’t include any rate limiting or throttling mechanisms. Without these safeguards, the server is vulnerable to being overwhelmed by excessive requests, potentially leading to service degradation.” Again, specificity elevates the discussion from a vague concern to actionable feedback.
# Example of DataLoader usage (Conceptual - not a complete solution)
from dataloader import DataLoader
dataloader = DataLoader(
# Configure DataLoader with appropriate settings
batch_size=10,
max_batches=50
)
def get_related_items(item_id):
"""Fetches related items using DataLoader for optimized data access."""
return dataloader.get(item_id) # returns a list of related objects efficiently
By focusing on precise language and communicating the impact of design decisions, you’ll dramatically improve your ability to collaborate effectively within a GraphQL development environment. It’s about demonstrating understanding, not just reciting definitions.