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

Improve documention of resolve function (info param) #799

Closed
odigity opened this Issue Apr 11, 2017 · 13 comments

Comments

Projects
None yet
9 participants
@odigity
Copy link

odigity commented Apr 11, 2017

I've read all the docs at graphql.org and the READMEs for graphql-js and express-graphql, and I cannot find a reference for the resolver function signature.

The GraphQL docs suggest:

(obj, args, context)

obj         The previous object, which for a field on the root Query type is often not used.
args        The arguments provided to the field in the GraphQL query.
context     A value which is provided to every resolver and holds important contextual information like the currently logged in user, or access to a database.

But my brief testing with my new express-graphql server I just made (first one) suggests the signature GraphQL.js uses is:

(args, context, obj)

From my console debugging:

JSON.stringify(args) -> { "foo": "bar" }
Object.keys(context) ->
    _consuming,     baseUrl,            method,         session,
    _dumped,        client,             next,           sessionCookies,
    _events,        complete,           originalUrl,    sessionKey,
    _eventsCount,   connection,         params,         sessionOptions,
    _maxListeners,  domain,             query,          socket,
    _parsedUrl,     headers,            rawHeaders,     statusCode,
    _passport,      httpVersion,        rawTrailers,    statusMessage,
    _readableState, httpVersionMajor,   read,           trailers,
                    httpVersionMinor,   readable,       upgrade,
                                        res,            url,
Object.keys(obj) -> fieldName, fieldNodes, fragments, operation, parentType, path, returnType, rootValue, schema, variableValues

(I just learned GraphQL/GraphQL.js last week, so I might be missing something obvious.)

@DxCx

This comment has been minimized.

Copy link

DxCx commented Apr 11, 2017

wait.. what?
what is the current signature of the function under debug in this case?
and how the scheme looks like?

anyway, resolver signature should be:
obj, args, context, info

obj -> the object resolved, (rootQuery argument for root types (such as Query))
args -> { [key: string]: any } GraphQL Arguments, nothing much to eleborate
context -> shared object used for side effects
info -> Object containing information about the execution info, (context) in your case above

@odigity

This comment has been minimized.

Copy link

odigity commented Apr 11, 2017

(Sorry, I had typos in the first post which I just fixed.)

What you described is not what I'm seeing. In my root resolvers, the first arg in the list is an object of input arguments for that field, and the second arg is clearly the context object.

@jonirrings

This comment has been minimized.

Copy link

jonirrings commented Apr 20, 2017

@odigity just have a look at the source code and you find the signature. that is the way I find it in v0.5 update, that's when I met the error too.

@wincent

This comment has been minimized.

Copy link
Contributor

wincent commented Apr 22, 2017

If you look at the source, you can see the resolve function is passed source, args, context, info. You can see some example resolvers in starWarsSchema, which is linked to from the README.

The best non-source documentation for this is probably the "Execution" page on graphql.org, although it doesn't mention the info parameter and should perhaps be updated. Anybody care to submit a PR for that?

@wincent wincent changed the title What is the resolver function signature? Improve documention of resolve function (info param) Apr 22, 2017

@odigity

This comment has been minimized.

Copy link

odigity commented Apr 22, 2017

As I explained in careful detail in my initial post, the signature you're describing does not the match the results I'm getting. For example, I'm seeing args in the first position.

@wincent

This comment has been minimized.

Copy link
Contributor

wincent commented Apr 22, 2017

Do you have some sample code that we could look at? The snippets in your post aren't something that I could run and try to repro what you're seeing.

@odigity

This comment has been minimized.

Copy link

odigity commented Apr 22, 2017

It'll take some time, but I'll try to get to it soon.

@jbinto

This comment has been minimized.

Copy link

jbinto commented Apr 27, 2017

The GraphQLResolveInfo flow type is useful:

export type GraphQLResolveInfo = {
fieldName: string;
fieldNodes: Array<FieldNode>;
returnType: GraphQLOutputType;
parentType: GraphQLCompositeType;
path: ResponsePath;
schema: GraphQLSchema;
fragments: { [fragmentName: string]: FragmentDefinitionNode };
rootValue: mixed;
operation: OperationDefinitionNode;
variableValues: { [variableName: string]: mixed };
};

@leebyron

This comment has been minimized.

Copy link
Collaborator

leebyron commented May 18, 2017

Closing this issue since it is starting to age.

The flow types and documentation should help, and you can also check out some of the examples and tests in this repository to see how resolvers are called with values.

What you might be running into is the default resolver, which attempts to look for a method on the source object with the name of the field requested, and if found calls that. In that case, source is not provided as a first argument since you can access that value with this.

@nikolasburk

This comment has been minimized.

Copy link

nikolasburk commented Feb 8, 2018

For those who are still stumbling upon this issue, I've written an article that documents the internals of the info object and explains its role in the query resolution process. You can find it here.

@stomvi

This comment has been minimized.

Copy link

stomvi commented Mar 21, 2018

@odigity I know what you were talking about. I was confused just like you were. I am using the buildSchema function from the graphql along with the express-graphql.

I didn't dive into the source code. The following is just my guess. Part of them are not listed clearly in the document but just a sentence:

When a resolver takes arguments, they are passed as one “args” object, as the first argument to the function.
-- Passing Arguments

I think the reason that all the first argument parentValue aka root were all omitted on purpose is that the value passed to the 1st level resolver is always null if not otherwise specified (see GraphQL Server Basics: The Schema), and all the resolvers (existing as methods of a class) from other levels get their parent values from the constructor. So still they just need the args as their first argument.

@Alappin

This comment has been minimized.

Copy link

Alappin commented Mar 26, 2018

I have figured it out. If you do not explicity provide your own resolver, then then default resolver is used. This the behaviour when you use buildschema. What then happens is the default resolver will call your resolvers with (args, context, info). In order to access the root value, you would need to use the this object in you resolver as stated by @leebyron. Note that you need to use function() {} when defining the resolver otherwise an arrow function will have an empty this object.

@stomvi

This comment has been minimized.

Copy link

stomvi commented Apr 2, 2018

@Alappin you are right. I listed I'm using express-graphql along with the graphq-js utility buildSchema and tried to dive into the source code. I only described the symptoms and to whom wants to know why, here's my situation:

When we use buildSchema(), we don't define resolvers in the definition. The schema will use default resolvers:
http://graphql.org/graphql-js/utilities/#buildschema

Instead we have to provide rootValue to the graphql() function as described here:
https://github.com/graphql/express-graphql#options

Since no real resolvers are provided to the execute, the defaultFieldResolver will be used during resolving:

const resolveFn = fieldDef.resolve || defaultFieldResolver;

When we think the resolveFn is called with the root or parent value as the first param:

return resolveFn(source, args, context, info);

instead the function is actually a defaultFieldResolver. It's like a proxy which calls the contained function by the matched key of the rootValue or just return the value:

return source[info.fieldName](args, context, info);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment