Skip to content

Commit e6cd9fc

Browse files
docs: anatomy of a resolver (#4381)
adds anatomy of a resolver guide This is a part of the effort to expand GraphQL.js documentation
1 parent c18e9f6 commit e6cd9fc

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

website/pages/docs/_meta.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ const meta = {
1919
'constructing-types': '',
2020
'oneof-input-objects': '',
2121
'defer-stream': '',
22+
'resolver-anatomy': '',
2223
'-- 3': {
2324
type: 'separator',
2425
title: 'FAQ',
+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
title: Anatomy of a Resolver
3+
---
4+
5+
# Anatomy of a Resolver
6+
7+
In GraphQL.js, a resolver is a function that returns the value for a
8+
specific field in a schema. Resolvers connect a GraphQL query to the
9+
underlying data or logic needed to fulfill it.
10+
11+
This guide breaks down the anatomy of a resolver, how GraphQL.js uses
12+
them during query execution, and best practices for writing them effectively.
13+
14+
## What is a resolver?
15+
16+
A resolver is responsible for returning the value for a specific field in a
17+
GraphQL query. During execution, GraphQL.js calls a resolver for each field,
18+
either using a custom function you provide or falling back to a default
19+
behavior.
20+
21+
If no resolver is provided, GraphQL.js tries to retrieve a property from the
22+
parent object that matches the field name. If the property is a function, it
23+
calls the function and uses the result. Otherwise, it returns the property
24+
value directly.
25+
26+
You can think of a resolver as a translator between the schema and the
27+
actual data. The schema defines what can be queried, while resolvers
28+
determine how to fetch or compute the data at runtime.
29+
30+
## Resolver function signature
31+
32+
When GraphQL.js executes a resolver, it calls the resolver function
33+
with four arguments:
34+
35+
```js
36+
function resolver(source, args, context, info) { ... }
37+
```
38+
39+
Each argument provides information that can help the resolver return the
40+
correct value:
41+
42+
- `source`: The result from the parent field's resolver. In nested fields,
43+
`source` contains the value returned by the parent object. For root fields,
44+
it is often `undefined`.
45+
- `args`: An object containing the arguments passed to the field in the
46+
query. For example, if a field is defined to accept an `id` argument, you can
47+
access it as `args.id`.
48+
- `context`: A shared object available to every resolver in an operation.
49+
It is commonly used to store per-request state like authentication
50+
information, database connections, or caching utilities.
51+
- `info`: Information about the current execution state, including
52+
the field name, path to the field from the root, the return type, the parent
53+
type, and the full schema. It is mainly useful for advanced scenarios such
54+
as query optimization or logging.
55+
56+
Resolvers can use any combination of these arguments, depending on the needs
57+
of the field they are resolving.
58+
59+
## Default resolvers
60+
61+
If you do not provide a resolver for a field, GraphQL.js uses a built-in
62+
default resolver called `defaultFieldResolver`.
63+
64+
The default behavior is simple:
65+
66+
- It looks for a property on the `source` object that matches the name of
67+
the field.
68+
- If the property exists and is a function, it calls the function and uses the
69+
result.
70+
- Otherwise, it returns the property value directly.
71+
72+
This default resolution makes it easy to build simple schemas without
73+
writing custom resolvers for every field. For example, if your `source` object
74+
already contains fields with matching names, GraphQL.js can resolve them
75+
automatically.
76+
77+
You can override the default behavior by specifying a `resolve` function when
78+
defining a field in the schema. This is necessary when the field’s value
79+
needs to be computed dynamically, fetched from an external service, or
80+
otherwise requires custom logic.
81+
82+
## Writing a custom resolver
83+
84+
A custom resolver is a function you define to control exactly how a field's
85+
value is fetched or computed. You can add a resolver by specifying a `resolve`
86+
function when defining a field in your schema:
87+
88+
```js
89+
const UserType = new GraphQLObjectType({
90+
name: 'User',
91+
fields: {
92+
fullName: {
93+
type: GraphQLString,
94+
resolve(source) {
95+
return `${source.firstName} ${source.lastName}`;
96+
},
97+
},
98+
},
99+
});
100+
```
101+
102+
Resolvers can be synchronous or asynchronous. If a resolver returns a
103+
Promise, GraphQL.js automatically waits for the Promise to resolve before
104+
continuing execution:
105+
106+
```js
107+
resolve(source, args, context) {
108+
return database.getUserById(args.id);
109+
}
110+
```
111+
112+
Custom resolvers are often used to implement patterns such as batching,
113+
caching, or delegation. For example, a resolver might use a batching utility
114+
like DataLoader to fetch multiple related records efficiently, or delegate
115+
part of the query to another API or service.
116+
117+
## Best practices
118+
119+
When writing resolvers, it's important to keep them focused and maintainable:
120+
121+
- Keep business logic separate. A resolver should focus on fetching or
122+
computing the value for a field, not on implementing business rules or
123+
complex workflows. Move business logic into separate service layers
124+
whenever possible.
125+
- Handle errors carefully. Resolvers should catch and handle errors
126+
appropriately, either by throwing GraphQL errors or returning `null` values
127+
when fields are nullable. Avoid letting unhandled errors crash the server.
128+
- Use context effectively. Store shared per-request information, such as
129+
authentication data or database connections, in the `context` object rather
130+
than passing it manually between resolvers.
131+
- Prefer batching over nested requests. For fields that trigger multiple
132+
database or API calls, use batching strategies to minimize round trips and
133+
improve performance. A common solution for batching in GraphQL is [dataloader](https://github.com/graphql/dataloader).
134+
- Keep resolvers simple. Aim for resolvers to be small, composable functions
135+
that are easy to read, test, and reuse.
136+
137+
Following these practices helps keep your GraphQL server reliable, efficient,
138+
and easy to maintain as your schema grows.

0 commit comments

Comments
 (0)