Skip to content
This repository has been archived by the owner on Apr 15, 2020. It is now read-only.

Commit

Permalink
refactor(resolvers)
Browse files Browse the repository at this point in the history
Remove references to resolve functions in favor of the generic term "resolver."

Retain old exports for backwards compatibility.
  • Loading branch information
yaacovCR committed Jan 6, 2020
1 parent 9a220e6 commit fff22aa
Show file tree
Hide file tree
Showing 22 changed files with 101 additions and 96 deletions.
6 changes: 3 additions & 3 deletions designs/connectors.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This document is intended as a design document for people who want to write conn
This is a draft at the moment, and not the final document. Chances are that the spec will change as we learn about the better ways to build GraphQL servers. It should be pretty close to the final version though, so if you want to get started and build connectors for specific backends, this document is a good starting point.


Technically you could write a GraphQL server without connectors and models by writing all your logic directly into the resolve functions, but in most cases that's not ideal. Connectors and models are a way of organizing code in a GraphQL server, and you should use them to keep your server modular. If the need arises, you can always write optimized queries directly in your resolvers or models.
Technically you could write a GraphQL server without connectors and models by writing all your logic directly into the resolvers, but in most cases that's not ideal. Connectors and models are a way of organizing code in a GraphQL server, and you should use them to keep your server modular. If the need arises, you can always write optimized queries directly in your resolvers or models.

Let's use an example schema, because it's always easier to explain things with examples:
```
Expand Down Expand Up @@ -60,7 +60,7 @@ Both batching and caching are more important in GraphQL than in traditional endp

Models are the glue between connectors - which are backend-specific - and GraphQL types - which are app-specific. They are very similar to models in ORMs, such as Rails' Active Record.

Let's say for example that you have two types, Author and Post, which are both stored in MySQL. Rather than calling the MySQL connector directly from your resolve functions, you should create models for Author and Post, which use the MySQL connector. This additional level of abstraction helps separate the data fetching logic from the GraphQL schema, which makes reusing and refactoring it easier.
Let's say for example that you have two types, Author and Post, which are both stored in MySQL. Rather than calling the MySQL connector directly from your resolvers, you should create models for Author and Post, which use the MySQL connector. This additional level of abstraction helps separate the data fetching logic from the GraphQL schema, which makes reusing and refactoring it easier.

In the example schema above, the Authors model would have the following methods:
```
Expand Down Expand Up @@ -150,7 +150,7 @@ app.use('/graphql', apolloServer({
});
```

Step 4: Calling models in resolve functions
Step 4: Calling models in resolvers
```
function resolve(author, args, ctx){
return ctx.models.Author.getById(author.id, ctx);
Expand Down
6 changes: 3 additions & 3 deletions designs/graphql-decorator-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ Decorators can be selectively applied to:
* A specific field
* An argument

Decorators can modify the behavior of the parts of the schema they are applied to. Sometimes that requires modifying other parts of the schema. For instance, the @validateRange decorator modifies the behavior of the containing field's resolve function.
Decorators can modify the behavior of the parts of the schema they are applied to. Sometimes that requires modifying other parts of the schema. For instance, the @validateRange decorator modifies the behavior of the containing field's resolver.

In general, decorators either add, remove or modify an attribute of the thing they wrap. The most common type of decorator (e.g. @adminOnly, @log, @connector) will wrap one or more field's resolve functions to alter the execution behavior of the GraphQL schema, but other decorators (e.g. @description) may add attributes to a type, field or argument. It is also possible for a type decorator to add a field to the type (e.g. @id(fields: ["uuid"]) can add the __id field).
In general, decorators either add, remove or modify an attribute of the thing they wrap. The most common type of decorator (e.g. @adminOnly, @log, @connector) will wrap one or more field resolvers to alter the execution behavior of the GraphQL schema, but other decorators (e.g. @description) may add attributes to a type, field or argument. It is also possible for a type decorator to add a field to the type (e.g. @id(fields: ["uuid"]) can add the __id field).


## Schema decorator API
Expand Down Expand Up @@ -120,7 +120,7 @@ class SampleFieldDecorator extends SchemaDecorator {
return (wrappedThing, { schema, type, field, context }) => {
// use this.config ...
// use args
// modify wrappedThing's properties, resolve functions, etc.
// modify wrappedThing's properties, resolvers, etc.
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions docs/source/generate-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,14 +210,14 @@ const jsSchema = makeExecutableSchema({

- `parseOptions` is an optional argument which allows customization of parse when specifying `typeDefs` as a string.

- `allowUndefinedInResolve` is an optional argument, which is `true` by default. When set to `false`, causes your resolve functions to throw errors if they return undefined, which can help make debugging easier.
- `allowUndefinedInResolve` is an optional argument, which is `true` by default. When set to `false`, causes your resolver to throw errors if they return undefined, which can help make debugging easier.

- `resolverValidationOptions` is an optional argument which accepts an `ResolverValidationOptions` object which has the following boolean properties:
- `requireResolversForArgs` will cause `makeExecutableSchema` to throw an error if no resolve function is defined for a field that has arguments.
- `requireResolversForArgs` will cause `makeExecutableSchema` to throw an error if no resolver is defined for a field that has arguments.

- `requireResolversForNonScalar` will cause `makeExecutableSchema` to throw an error if a non-scalar field has no resolver defined. Setting this to `true` can be helpful in catching errors, but defaults to `false` to avoid confusing behavior for those coming from other GraphQL libraries.

- `requireResolversForAllFields` asserts that *all* fields have a valid resolve function.
- `requireResolversForAllFields` asserts that *all* fields have valid resolvers.

- `requireResolversForResolveType` will require a `resolveType()` method for Interface and Union types. This can be passed in with the field resolvers as `__resolveType()`. False to disable the warning.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/mocking.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ addMockFunctionsToSchema({
});
```

Given an instance of GraphQLSchema and a mock object, `addMockFunctionsToSchema` modifies the schema in place to return mock data for any valid query that is sent to the server. If `mocks` is not passed, the defaults will be used for each of the scalar types. If `preserveResolvers` is set to `true`, existing resolve functions will not be overwritten to provide mock data. This can be used to mock some parts of the server and not others.
Given an instance of GraphQLSchema and a mock object, `addMockFunctionsToSchema` modifies the schema in place to return mock data for any valid query that is sent to the server. If `mocks` is not passed, the defaults will be used for each of the scalar types. If `preserveResolvers` is set to `true`, existing resolvers will not be overwritten to provide mock data. This can be used to mock some parts of the server and not others.

### MockList

Expand Down
16 changes: 8 additions & 8 deletions docs/source/resolvers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Keep in mind that GraphQL resolvers can return [promises](https://developer.mozi

## Resolver map

In order to respond to queries, a schema needs to have resolve functions for all fields. Resolve functions cannot be included in the GraphQL schema language, so they must be added separately. This collection of functions is called the "resolver map".
In order to respond to queries, a schema needs to have resolvers for all fields. Resolvers are per field functions that are given a parent object, arguments, and the execution context, and are responsible for returning a result for that field. Resolvers cannot be included in the GraphQL schema language, so they must be added separately. The collection of resolvers is called the "resolver map".

The `resolverMap` object (`IResolvers`) should have a map of resolvers for each relevant GraphQL Object Type. The following is an example of a valid `resolverMap` object:

Expand Down Expand Up @@ -53,7 +53,7 @@ Resolvers in GraphQL can return different kinds of results which are treated dif

1. `null` or `undefined` - this indicates the object could not be found. If your schema says that field is _nullable_, then the result will have a `null` value at that position. If the field is `non-null`, the result will "bubble up" to the nearest nullable field and that result will be set to `null`. This is to ensure that the API consumer never gets a `null` value when they were expecting a result.
2. An array - this is only valid if the schema indicates that the result of a field should be a list. The sub-selection of the query will run once for every item in this array.
3. A promise - resolvers often do asynchronous actions like fetching from a database or backend API, so they can return promises. This can be combined with arrays, so a resolver can return:
3. A promise - resolvers often do asynchronous actions like fetching from a database or backend API, so they can return promises. This can be combined with arrays, so a resolver can return:
1. A promise that resolves an array
2. An array of promises
4. A scalar or object value - a resolver can also return any other kind of value, which doesn't have any special meaning but is simply passed down into any nested resolvers, as described in the next section.
Expand Down Expand Up @@ -144,13 +144,13 @@ const resolverMap = {

In addition to using a resolver map with `makeExecutableSchema`, you can use it with any GraphQL.js schema by importing the following function from `graphql-tools`:

### addResolveFunctionsToSchema({ schema, resolvers, resolverValidationOptions?, inheritResolversFromInterfaces? })
### addResolversToSchema({ schema, resolvers, resolverValidationOptions?, inheritResolversFromInterfaces? })

`addResolveFunctionsToSchema` takes an options object of `IAddResolveFunctionsToSchemaOptions` and modifies the schema in place by attaching the resolvers to the relevant types.
`addResolversToSchema` takes an options object of `IAddResolveFunctionsToSchemaOptions` and modifies the schema in place by attaching the resolvers to the relevant types.


```js
import { addResolveFunctionsToSchema } from 'graphql-tools';
import { addResolversToSchema } from 'graphql-tools';

const resolvers = {
RootQuery: {
Expand All @@ -162,7 +162,7 @@ const resolvers = {
},
};

addResolveFunctionsToSchema({ schema, resolvers });
addResolversToSchema({ schema, resolvers });
```

The `IAddResolveFunctionsToSchemaOptions` object has 4 properties that are described in [`makeExecutableSchema`](/generate-schema/#makeexecutableschemaoptions).
Expand All @@ -175,9 +175,9 @@ export interface IAddResolveFunctionsToSchemaOptions {
}
```

### addSchemaLevelResolveFunction(schema, rootResolveFunction)
### addSchemaLevelResolver(schema, rootResolveFunction)

Some operations, such as authentication, need to be done only once per query. Logically, these operations belong in an obj resolve function, but unfortunately GraphQL-JS does not let you define one. `addSchemaLevelResolveFunction` solves this by modifying the GraphQLSchema that is passed as the first argument.
Some operations, such as authentication, need to be done only once per query. Logically, these operations belong in a schema level resolver field resolver, but unfortunately GraphQL-JS does not let you define one. `addSchemaLevelResolver` solves this by modifying the GraphQLSchema that is passed as the first argument.

## Companion tools

Expand Down
2 changes: 1 addition & 1 deletion docs/source/schema-stitching.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,4 +444,4 @@ When using schema transforms, `onTypeConflict` is often unnecessary, since trans

#### inheritResolversFromInterfaces

The `inheritResolversFromInterfaces` option is simply passed through to `addResolveFunctionsToSchema`, which is called when adding resolvers to the schema under the covers. See [`addResolveFunctionsToSchema`](/resolvers/#addresolvefunctionstoschema-schema-resolvers-resolvervalidationoptions-inheritresolversfrominterfaces-) for more info.
The `inheritResolversFromInterfaces` option is simply passed through to `addResolversToSchema`, which is called when adding resolvers to the schema under the covers. See [`addResolversToSchema`](/resolvers/#addresolvefunctionstoschema-schema-resolvers-resolvervalidationoptions-inheritresolversfrominterfaces-) for more info.
2 changes: 1 addition & 1 deletion docs/source/schema-transforms.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ RenameRootFields(

### Modifying object fields

* `TransformObjectFields(objectFieldTransformer: ObjectFieldTransformer, fieldNodeTransformer?: FieldNodeTransformer))`: Given an object field transformer, arbitrarily transform fields. The `objectFieldTransformer` can return a `GraphQLFieldConfig` definition, a object with new `name` and a `field`, `null` to remove the field, or `undefined` to leave the field unchanged. The optional `fieldNodeTransformer`, if specified, is called upon any field of that type in the request; result transformation can be specified by wrapping the resolve function within the `objectFieldTransformer`. In this way, a field can be fully arbitrarily modified in place.
* `TransformObjectFields(objectFieldTransformer: ObjectFieldTransformer, fieldNodeTransformer?: FieldNodeTransformer))`: Given an object field transformer, arbitrarily transform fields. The `objectFieldTransformer` can return a `GraphQLFieldConfig` definition, a object with new `name` and a `field`, `null` to remove the field, or `undefined` to leave the field unchanged. The optional `fieldNodeTransformer`, if specified, is called upon any field of that type in the request; result transformation can be specified by wrapping the field's resolver within the `objectFieldTransformer`. In this way, a field can be fully arbitrarily modified in place.

```ts
TransformObjectFields(objectFieldTransformer: ObjectFieldTransformer, fieldNodeTransformer: FieldNodeTransformer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ import {
forEachDefaultValue,
} from '../utils';

function addResolveFunctionsToSchema(
function addResolversToSchema(
options: IAddResolveFunctionsToSchemaOptions | GraphQLSchema,
legacyInputResolvers?: IResolvers,
legacyInputValidationOptions?: IResolverValidationOptions,
) {
if (options instanceof GraphQLSchema) {
console.warn(
'The addResolveFunctionsToSchema function takes named options now; see IAddResolveFunctionsToSchemaOptions',
'The addResolversToSchema function takes named options now; see IAddResolveFunctionsToSchemaOptions',
);
options = {
schema: options,
Expand Down Expand Up @@ -216,4 +216,4 @@ function setFieldProperties(
});
}

export default addResolveFunctionsToSchema;
export default addResolversToSchema;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import {
GraphQLFieldResolver,
} from 'graphql';

// wraps all resolve functions of query, mutation or subscription fields
// with the provided function to simulate a root schema level resolve funciton
function addSchemaLevelResolveFunction(
// wraps all resolvers of query, mutation or subscription fields
// with the provided function to simulate a root schema level resolver
function addSchemaLevelResolver(
schema: GraphQLSchema,
fn: GraphQLFieldResolver<any, any>,
): void {
Expand Down Expand Up @@ -74,4 +74,4 @@ function runAtMostOncePerRequest(
};
}

export default addSchemaLevelResolveFunction;
export default addSchemaLevelResolver;
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { IResolverValidationOptions } from '../Interfaces';
import { forEachField } from '../utils';
import SchemaError from './SchemaError';

function assertResolveFunctionsPresent(
function assertResolversPresent(
schema: GraphQLSchema,
resolverValidationOptions: IResolverValidationOptions = {},
) {
Expand All @@ -30,17 +30,17 @@ function assertResolveFunctionsPresent(
}

forEachField(schema, (field, typeName, fieldName) => {
// requires a resolve function for *every* field.
// requires a resolver for *every* field.
if (requireResolversForAllFields) {
expectResolveFunction(field, typeName, fieldName);
}

// requires a resolve function on every field that has arguments
// requires a resolver on every field that has arguments
if (requireResolversForArgs && field.args.length > 0) {
expectResolveFunction(field, typeName, fieldName);
}

// requires a resolve function on every field that returns a non-scalar type
// requires a resolver on every field that returns a non-scalar type
if (
requireResolversForNonScalar &&
!(getNamedType(field.type) instanceof GraphQLScalarType)
Expand All @@ -58,7 +58,7 @@ function expectResolveFunction(
if (!field.resolve) {
console.warn(
// tslint:disable-next-line: max-line-length
`Resolve function missing for "${typeName}.${fieldName}". To disable this warning check https://github.com/apollostack/graphql-tools/issues/131`,
`Resolver missing for "${typeName}.${fieldName}". To disable this warning check https://github.com/apollostack/graphql-tools/issues/131`,
);
return;
}
Expand All @@ -69,4 +69,4 @@ function expectResolveFunction(
}
}

export default assertResolveFunctionsPresent;
export default assertResolversPresent;
4 changes: 2 additions & 2 deletions src/generate/attachConnectorsToContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { deprecated } from 'deprecated-decorator';

import { IConnectors, IConnector, IConnectorCls } from '../Interfaces';

import addSchemaLevelResolveFunction from './addSchemaLevelResolveFunction';
import addSchemaLevelResolver from './addSchemaLevelResolver';

// takes a GraphQL-JS schema and an object of connectors, then attaches
// the connectors to the context by wrapping each query or mutation resolve
Expand Down Expand Up @@ -67,7 +67,7 @@ const attachConnectorsToContext = deprecated<Function>(
});
return root;
};
addSchemaLevelResolveFunction(schema, attachconnectorFn);
addSchemaLevelResolver(schema, attachconnectorFn);
},
);

Expand Down
2 changes: 1 addition & 1 deletion src/generate/decorateWithLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function decorateWithLogger(
return (root, args, ctx, info) => {
try {
const result = fn(root, args, ctx, info);
// If the resolve function returns a Promise log any Promise rejects.
// If the resolver returns a Promise log any Promise rejects.
if (
result &&
typeof result.then === 'function' &&
Expand Down
11 changes: 8 additions & 3 deletions src/generate/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { default as addResolveFunctionsToSchema } from './addResolveFunctionsToSchema';
export { default as addSchemaLevelResolveFunction } from './addSchemaLevelResolveFunction';
export { default as assertResolveFunctionsPresent } from './assertResolveFunctionsPresent';
export { default as addResolversToSchema } from './addResolversToSchema';
export { default as addSchemaLevelResolver } from './addSchemaLevelResolver';
export { default as assertResolversPresent } from './assertResolversPresent';
export { default as attachDirectiveResolvers } from './attachDirectiveResolvers';
export { default as attachConnectorsToContext } from './attachConnectorsToContext';
export { default as buildSchemaFromTypeDefinitions } from './buildSchemaFromTypeDefinitions';
Expand All @@ -11,3 +11,8 @@ export { default as decorateWithLogger } from './decorateWithLogger';
export { default as extendResolversFromInterfaces } from './extendResolversFromInterfaces';
export { default as extractExtensionDefinitions } from './extractExtensionDefinitions';
export { default as SchemaError } from './SchemaError';

// for backwards compatibility
export { default as addResolveFunctionsToSchema } from './addResolversToSchema';
export { default as addSchemaLevelResolveFunction } from './addSchemaLevelResolver';
export { default as assertResolveFunctionsPresent } from './assertResolversPresent';
Loading

0 comments on commit fff22aa

Please sign in to comment.