Resolver organization in GraphQL - Time & Space Complexity
When working with GraphQL, resolvers fetch data for each field requested. Understanding how the time to get data grows as requests get bigger helps us write faster APIs.
We want to know: how does the work done by resolvers change when the number of requested items increases?
Analyze the time complexity of the following resolver setup.
const resolvers = {
Query: {
users: () => fetchAllUsers(),
},
User: {
posts: (user) => fetchPostsByUserId(user.id),
},
};
This code fetches all users, then for each user fetches their posts separately.
Look for repeated work in the resolvers.
- Primary operation: Fetching posts for each user.
- How many times: Once per user, so if there are n users, this runs n times.
As the number of users grows, the number of post-fetching calls grows too.
| Input Size (n users) | Approx. Operations (post fetches) |
|---|---|
| 10 | 10 calls to fetch posts |
| 100 | 100 calls to fetch posts |
| 1000 | 1000 calls to fetch posts |
Pattern observation: The number of post fetches grows directly with the number of users.
Time Complexity: O(n)
This means the time to get all posts grows in a straight line as the number of users grows.
[X] Wrong: "Fetching posts inside each user resolver is just one operation regardless of user count."
[OK] Correct: Actually, the posts fetch runs once for every user, so the total work grows with the number of users.
Understanding how resolver calls multiply with data size helps you design efficient APIs and shows you can think about performance clearly.
"What if we batch fetch posts for all users in one call instead of one call per user? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of resolvers
Resolvers are functions that fetch or compute the data for each field in the GraphQL schema.Step 2: Identify what resolvers connect
Resolvers connect the schema fields to the actual data sources like databases or APIs.Final Answer:
To connect schema fields to the actual data sources -> Option AQuick Check:
Resolvers = Connect schema to data [OK]
- Confusing resolvers with schema definitions
- Thinking resolvers handle UI or styling
- Assuming resolvers manage client state
Solution
Step 1: Recognize common resolver organization
Resolvers are typically grouped by operation types: Query, Mutation, and by object types for clarity.Step 2: Evaluate the options
Grouping by Query, Mutation, and types helps maintain and scale the server easily.Final Answer:
Group resolvers by Query, Mutation, and type names -> Option AQuick Check:
Organize resolvers by operation type [OK]
- Putting all resolvers in one list without grouping
- Mixing resolver code inside schema files
- Using a single resolver for all fields
const resolvers = {
Query: {
user: () => ({ id: 1, name: "Alice" })
},
User: {
fullName: (parent) => parent.name + " Smith"
}
};
What will be the result of querying { user { fullName } }?Solution
Step 1: Understand the Query resolver
The Query resolver returns a user object with id 1 and name "Alice".Step 2: Understand the User type resolver
The User resolver for fullName appends " Smith" to the parent's name, resulting in "Alice Smith".Final Answer:
{ "user": { "fullName": "Alice Smith" } } -> Option DQuick Check:
fullName = parent.name + " Smith" [OK]
- Forgetting to use parent argument in field resolver
- Expecting fullName to be directly in Query resolver
- Assuming missing resolver causes error
const resolvers = {
Query: {
posts: () => fetchPosts()
},
Post: {
author: () => fetchAuthor()
}
};
What is the main problem with this resolver organization?Solution
Step 1: Analyze Post.author resolver
The author resolver does not use the parent post argument, so it cannot fetch the correct author for each post.Step 2: Understand resolver context
Field resolvers should use the parent argument to access related data; ignoring it causes wrong or unrelated data.Final Answer:
The Post.author resolver ignores the parent post and fetches unrelated author data -> Option BQuick Check:
Use parent argument in nested resolvers [OK]
- Ignoring parent argument in nested resolvers
- Misplacing Query resolvers inside Mutation
- Assuming all resolvers must be async
Book. Which structure below best supports maintainability and scalability?Solution
Step 1: Identify best practice for resolver organization
Grouping resolvers by operation type (Query, Mutation) and by custom types (Book) keeps code clear and modular.Step 2: Compare options
const resolvers = { Query: {...}, Mutation: {...}, Book: {...} }; clearly separates concerns and allows easy updates and scaling.Final Answer:
const resolvers = { Query: {...}, Mutation: {...}, Book: {...} }; -> Option CQuick Check:
Separate resolvers by operation and type [OK]
- Putting all resolvers in one object without grouping
- Omitting resolvers for custom types
- Ignoring Mutation resolvers
Ready to prove it?
Pass the test and this pattern moves unknown
