1
votes

Using apollo-server-express and graphql-tools, I am attempting to create a minimally viable schema from a JSON object:

const books = [
  {
    "title": "Harry Potter",
    "author": 'J.K. Rowling',
    "slug": "harry_potter",
  },
  {
    "title": 'Jurassic Park',
    "author": 'Michael Crichton',
    "slug": "jurassic_park",
  },
];

// The GraphQL schema in string form
const typeDefs = `
  type Query {
    books: [Book]
    book(title: String!): Book
  }
  type Book { title: String!, author: String!, slug: String! }
`;

// The resolvers
const resolvers = {
  Query: {
    books: () => books,
    book: (_, { title }) => books.filter(book => {
      return new Promise((resolve, reject) => {
        if(book.title == title) {
          console.log('hack log resolve book _: ', JSON.stringify(book))
          resolve(JSON.stringify(book));
        }
      })
    }),
   },
   Book: {
     title: (root, args, context, info) => {
       //args is empty, need to match arg w book.title
       /*
       context:  {
        _extensionStack:
         GraphQLExtensionStack {
           extensions: [ [FormatErrorExtension], [CacheControlExtension] ]
         }
       }
       , root,
       */
       console.log('resolve Book args: ', args, 'info', info);//JSON.stringify(root.book))
       return books.filter(book => {
         if(book.title == root.title) {
           return book;
         }
       });//JSON.stringify({"title": root.title});
     }
   }
};
//    book: (_, { title }) => books.filter(book => book.title == title),


// Put together a schema
const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

This is my repository.

When logging and stepping through node_modules/graphql/execution/execute.js, the first param of execute argsOrSchema.variableValues contains the query argument key and value, however the 5th argument variableValues is undefined.

According to some threads such as this GitHub issue I can pull the variableValues from the info argument of my resolver, however I would still like to know why the args object is empty?

Here is a gist of the info log given by GraphQL in the resolver function

1

1 Answers

3
votes

The args parameter is populated by the arguments passed to the field being resolved -- any arguments passed to other fields will not be included in the args parameter.

Your schema includes a single argument (title) on the book field of your Query type. That means the resolver for that field will receive the title argument as part of its args parameter, but only if that argument is actually included in your query:

// Request
query {
  book(title: "Something") {
    title
  }
}

// Resolvers
const resolvers = {
  Query: {
    book: (root, args) => {
      console.log(args) // {title: 'Something'}
    }
  },
}

As opposed to:

// Request
query {
  book {
    title
  }
}

// Resolvers
const resolvers = {
  Query: {
    book: (root, args) => {
      console.log(args) // {}
    }
  },
}

If you pass in a value for the title argument, the only way to get that value in resolvers for other fields is to parse the info parameter. You would not look at the variableValues property, though because the value passed to an argument could be a literal value or a variable. You'd need to traverse the fieldNodes array and locate the appropriate argument value instead.

However, there's typically no need to go through all that.

If the book field is supposed to just a return a book object, your logic for selecting the right book from the books array should be included in that field's resolver:

const resolvers = {
  Query: {
    book: (root, args) => {
      return books.find(book => book.title === args.title)
    }
  },
}

There is no reason to include a resolver for the title field on the Book type, unless you need that field to resolve to something other than what it will resolve to by default (the title property on the object returned by the parent field's resolver). This would be sufficient to query all books and an individual book by title:

const resolvers = {
  Query: {
    book: (root, args) => {
      return books.find(book => book.title === args.title)
    },
    books: () => books,
  },
}

Check out the official tutorial from Apollo for more examples and a complete explanation of how resolvers work.