25

Given the following apollo server graphql schema I wanted to break these down into separate modules so I don't want the author query under the root Query schema.. and want it separated. So i added another layer called authorQueries before adding it to the Root Query

type Author {
    id: Int,
    firstName: String,
    lastName: String
}  
type authorQueries {
    author(firstName: String, lastName: String): Author
}

type Query {
    authorQueries: authorQueries
}

schema {
    query: Query
}

I tried the following.. you can see that authorQueries was added as another layer before the author function is specified.

Query: {
    authorQueries :{
        author (root, args) {
            return {}
       }
    }
}

When querying in Graphiql, I also added that extra layer..

{
    authorQueries {
        author(firstName: "Stephen") {
            id
        }
    }
}

I get the following error.

"message": "Resolve function for \"Query.authorQueries\" returned undefined",

2
  • Related Apollo docs: apollographql.com/docs/apollo-server/data/resolvers/… Commented Oct 11, 2020 at 15:39
  • Just wondering If It's good for designing gql like this. authorQueires, productQueries...etc or by authorization meaning. I think It's not bad, but less seen. Commented Oct 21, 2021 at 9:33

3 Answers 3

30

To create a "nested" resolver, simply define the resolver on the return type of the parent field. In this case, your authorQueries field returns the type authorQueries, so you can put your resolver there:

{
  Query: { authorQueries: () => ({}) },
  authorQueries: {
    author(root, args) {
      return "Hello, world!";
    }
  }
}

So in the technical sense, there is no such thing as a nested resolver - every object type has a flat list of fields, and those fields have return types. The nesting of the GraphQL query is what makes the result nested.

Sign up to request clarification or add additional context in comments.

6 Comments

Is there not an better way, like defining a resolver for the author type, which uses some values of the overgiven root parameter? When i do it like this i have make a resolver for everytime a auther is used in an other type
Yep, you need to define a resolver for every field that returns an Author, at least in current GraphQL implementations. Could be better in the future.
I like your angle about GraphQL not having "nested" resolvers. I had struggled through quite a lot of documentations until I realized that..
This does not work for depth > 1. Resolvers can only be defined for properties on the type returned from the query, they cannot be defined for properties on these properties. Unless i'm missing something, this is a major performance issue as you end up performing computations that aren't necessary on every request.
I have tested authorQueries: () => ({}) returning the empty object actually worked. If TypeScript complains, you can simply do the casting: authorQueries: () => ({} as authorQueries)
|
5

Query.libraries() > Library.books() > Book.author() > Author.name()

Apollo Official related docs (Great example inside):

Resolver chains

https://www.apollographql.com/docs/apollo-server/data/resolvers/#resolver-chains

/* code from:
https://www.apollographql.com/docs/apollo-server/data/resolvers/#resolver-chains
*/

const { ApolloServer, gql } = require('apollo-server');

const libraries = [
  {
    branch: 'downtown'
  },
  {
    branch: 'riverside'
  },
];

// The branch field of a book indicates which library has it in stock
const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin',
    branch: 'riverside'
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster',
    branch: 'downtown'
  },
];

// Schema definition
const typeDefs = gql`

# A library has a branch and books
  type Library {
    branch: String!
    books: [Book!]
  }

  # A book has a title and author
  type Book {
    title: String!
    author: Author!
  }

  # An author has a name
  type Author {
    name: String!
  }

  # Queries can fetch a list of libraries
  type Query {
    libraries: [Library]
  }
`;

// Resolver map
const resolvers = {
  Query: {
    libraries() {

      // Return our hardcoded array of libraries
      return libraries;
    }
  },
  Library: {
    books(parent) {

      // Filter the hardcoded array of books to only include
      // books that are located at the correct branch
      return books.filter(book => book.branch === parent.branch);
    }
  },
  Book: {

    // The parent resolver (Library.books) returns an object with the
    // author's name in the "author" field. Return a JSON object containing
    // the name, because this field expects an object.
    author(parent) {
      return {
        name: parent.author
      };
    }
  }

  // Because Book.author returns an object with a "name" field,
  // Apollo Server's default resolver for Author.name will work.
  // We don't need to define one.
};

// Pass schema definition and resolvers to the
// ApolloServer constructor
const server = new ApolloServer({ typeDefs, resolvers });

// Launch the server
server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

Comments

1

I found that returning functions on the parent fields return type results in the this arg being bound, and breaks the resolver interface b/c the nested resolver doesn't the parent as the first argument.

For inline type definitions

import {
  graphql,
} from 'graphql';

import {
  makeExecutableSchema, IResolverObject
} from 'graphql-tools';

const types = `
type Query {
  person: User
}

type User {
  id: ID
  name: String,
  dog(showCollar: Boolean): Dog
}

type Dog {
  name: String
}
`;

const User: IResolverObject = {
  dog(obj, args, ctx) {
    console.log('Dog Arg 1', obj);
    return {
      name: 'doggy'
    };
  }
};

const resolvers = {
  User,
  Query: {
    person(obj) {
      console.log('Person Arg 1', obj);
      return {
        id: 'foo',
        name: 'bar',
      };
    }
  }
};

const schema = makeExecutableSchema({
  typeDefs: [types],
  resolvers
});

const query = `{ 
  person {
    name,
    dog(showCollar: true) {
      name
    }
  }
 }`;


graphql(schema, query).then(result => {
  console.log(JSON.stringify(result, null, 2));
});

// Person Arg 1 undefined
// Dog Arg 1 { id: 'foo', name: 'bar' }
// {
//   "data": {
//     "person": {
//       "name": "bar",
//       "dog": {
//         "name": "doggy"
//       }
//     }
//   }
// }

You can also use addResolveFunctionsToSchema as seen in the below gist.

https://gist.github.com/blugavere/4060f4bf2f3d5b741c639977821a254f

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.