|
| 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