Explanation for different implementations of resolver function in graphql

前端 未结 1 702
忘掉有多难
忘掉有多难 2020-12-18 08:36

I\'ve been reading through the graphQL docs and found that they\'ve explained the implementation of the graphql server in 2 ways: one using graphql-yoga which is a fully fea

相关标签:
1条回答
  • 2020-12-18 09:26

    REAL TALK: the GraphQL.js docs are not that great. In my opinion, they never should have used examples with buildSchema in the first place because it understandably leads to this kind of confusion.

    GraphQL.js (i.e. the graphql package) is the JavaScript implementation of GraphQL. Building a schema in GraphQL.js is done programmatically, by constructing an instance of the GraphQLSchema class:

    const userType = new GraphQLObjectType({
      name: 'User',
      fields: {
        id: {
          type: GraphQLID,
        },
        email: {
          type: GraphQLString,
        },
      },
    });
    const queryType = new GraphQLObjectType({
      name: 'Query',
      fields: {
        user: {
          type: userType,
          resolve: () => ({ id: 1, email: 'john.doe@example.com' }),
        },
      },
    });
    const schema = new GraphQLSchema({
      query: queryType,
    })
    

    If we print this schema in Schema Definition Language (SDL), it looks like this:

    type Query {
      user: User
    }
    
    type User {
      id: ID
      email: String
    }
    

    Working with SDL is much easier than having to write out all that code. However, GraphQL.js does not provide a way to build a fully-featured schema from SDL. It does provide a buildSchema function, but this utility constructs a schema without any resolvers (and a number of other features like union/interface type resolution).

    The graphql-tools package provides a makeExecutableSchema function that lets you build a schema from SDL and a resolver map object. This is what's used under the hood by apollo-server and graphql-yoga. makeExecutableSchema constructs a schema from SDL using buildSchema and then mutates the resulting object, adding the resolvers in after the fact.

    In GraphQL.js, the resolve function (or resolver) for a field takes four parameters -- the parent value, the field's arguments, the context and a GraphQLResolveInfo object. If we're creating a GraphQLObjectType like userType in the above example, this is the optional function we can provide for each of the fields in our object. This is the same function you define when you construct a resolver map to use with graphql-yoga. This is the only implementation of a field resolver.

    So what's the deal with buildSchema??

    The examples in the docs take advantage of GraphQL's default field resolver:

    export const defaultFieldResolver: GraphQLFieldResolver<any, *> = function(
      source,
      args,
      contextValue,
      info,
    ) {
      if (typeof source === 'object' || typeof source === 'function') {
        const property = source[info.fieldName];
        if (typeof property === 'function') {
          return source[info.fieldName](args, contextValue, info);
        }
        return property;
      }
    };
    

    As you can see, the default resolution logic looks for a property with the same name as the field on the source (parent) value. In our example above, the user resolver returns {id: 1, email: 'john.doe@example.com'} -- this is the value the field resolves to. The field is of the type User. We do not have a resolver defined for our id field, so the default resolver does its thing. The id field resolves to 1 because that's the value of the property named id on the parent object the resolver receives.

    However, the parent value can also be a function instead of an object. If it's a function, it gets called first and then the return value is used. What does the function get called with? Well, it can't pass it a parent value (because of infinite recursion), but it can pass it the remaining three parameters (args, context and info). So that's what it does.

    Now for the magic trick

    0 讨论(0)
提交回复
热议问题