-
Notifications
You must be signed in to change notification settings - Fork 2k
docs: anatomy of a resolver #4381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
--- | ||
title: Anatomy of a Resolver | ||
--- | ||
|
||
# Anatomy of a Resolver | ||
|
||
In GraphQL.js, a resolver is a function that returns the value for a | ||
specific field in a schema. Resolvers connect a GraphQL query to the | ||
underlying data or logic needed to fulfill it. | ||
|
||
This guide breaks down the anatomy of a resolver, how GraphQL.js uses | ||
them during query execution, and best practices for writing them effectively. | ||
|
||
## What is a resolver? | ||
|
||
A resolver is responsible for returning the value for a specific field in a | ||
GraphQL query. During execution, GraphQL.js calls a resolver for each field, | ||
either using a custom function you provide or falling back to a default | ||
behavior. | ||
|
||
If no resolver is provided, GraphQL.js tries to retrieve a property from the | ||
parent object that matches the field name. If the property is a function, it | ||
calls the function and uses the result. Otherwise, it returns the property | ||
value directly. | ||
|
||
You can think of a resolver as a translator between the schema and the | ||
actual data. The schema defines what can be queried, while resolvers | ||
determine how to fetch or compute the data at runtime. | ||
|
||
## Resolver function signature | ||
|
||
When GraphQL.js executes a resolver, it calls the resolver function | ||
with four arguments: | ||
|
||
```js | ||
function resolver(source, args, context, info) { ... } | ||
``` | ||
|
||
Each argument provides information that can help the resolver return the | ||
correct value: | ||
|
||
- `source`: The result from the parent field's resolver. In nested fields, | ||
`source` contains the value returned by the parent object. For root fields, | ||
it is often `undefined`. | ||
- `args`: An object containing the arguments passed to the field in the | ||
query. For example, if a field is defined to accept an `id` argument, you can | ||
access it as `args.id`. | ||
- `context`: A shared object available to every resolver in an operation. | ||
It is commonly used to store per-request state like authentication | ||
information, database connections, or caching utilities. | ||
- `info`: Information about the current execution state, including | ||
the field name, path to the field from the root, the return type, the parent | ||
type, and the full schema. It is mainly useful for advanced scenarios such | ||
as query optimization or logging. | ||
|
||
Resolvers can use any combination of these arguments, depending on the needs | ||
of the field they are resolving. | ||
|
||
## Default resolvers | ||
|
||
If you do not provide a resolver for a field, GraphQL.js uses a built-in | ||
default resolver called `defaultFieldResolver`. | ||
|
||
The default behavior is simple: | ||
|
||
- It looks for a property on the `source` object that matches the name of | ||
the field. | ||
- If the property exists and is a function, it calls the function and uses the | ||
result. | ||
- Otherwise, it returns the property value directly. | ||
|
||
This default resolution makes it easy to build simple schemas without | ||
writing custom resolvers for every field. For example, if your `source` object | ||
already contains fields with matching names, GraphQL.js can resolve them | ||
automatically. | ||
|
||
You can override the default behavior by specifying a `resolve` function when | ||
defining a field in the schema. This is necessary when the field’s value | ||
needs to be computed dynamically, fetched from an external service, or | ||
otherwise requires custom logic. | ||
|
||
## Writing a custom resolver | ||
|
||
A custom resolver is a function you define to control exactly how a field's | ||
value is fetched or computed. You can add a resolver by specifying a `resolve` | ||
function when defining a field in your schema: | ||
|
||
```js | ||
const UserType = new GraphQLObjectType({ | ||
name: 'User', | ||
fields: { | ||
fullName: { | ||
type: GraphQLString, | ||
resolve(source) { | ||
return `${source.firstName} ${source.lastName}`; | ||
}, | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
Resolvers can be synchronous or asynchronous. If a resolver returns a | ||
Promise, GraphQL.js automatically waits for the Promise to resolve before | ||
continuing execution: | ||
|
||
```js | ||
resolve(source, args, context) { | ||
return database.getUserById(args.id); | ||
} | ||
``` | ||
|
||
Custom resolvers are often used to implement patterns such as batching, | ||
caching, or delegation. For example, a resolver might use a batching utility | ||
like DataLoader to fetch multiple related records efficiently, or delegate | ||
part of the query to another API or service. | ||
|
||
## Best practices | ||
|
||
When writing resolvers, it's important to keep them focused and maintainable: | ||
|
||
- Keep business logic separate. A resolver should focus on fetching or | ||
computing the value for a field, not on implementing business rules or | ||
complex workflows. Move business logic into separate service layers | ||
whenever possible. | ||
- Handle errors carefully. Resolvers should catch and handle errors | ||
appropriately, either by throwing GraphQL errors or returning `null` values | ||
when fields are nullable. Avoid letting unhandled errors crash the server. | ||
- Use context effectively. Store shared per-request information, such as | ||
authentication data or database connections, in the `context` object rather | ||
than passing it manually between resolvers. | ||
- Prefer batching over nested requests. For fields that trigger multiple | ||
database or API calls, use batching strategies to minimize round trips and | ||
improve performance. A common solution for batching in GraphQL is [dataloader](https://github.com/graphql/dataloader). | ||
- Keep resolvers simple. Aim for resolvers to be small, composable functions | ||
that are easy to read, test, and reuse. | ||
|
||
Following these practices helps keep your GraphQL server reliable, efficient, | ||
and easy to maintain as your schema grows. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not totally clear to me what this sentence means?