GraphQL fields as a function
Asked Answered
A

2

10

I am studying GraphQL and I get a bit confused from different implementations on the specific issue when writing the fields of a GraphQLObjectType.
What is the difference between these two implementations?

1.

var schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {  // as object
      echo: {
        type: GraphQLString,
        args: {
          email: { type: EmailType }
        },
        resolve: (root, {email}) => {
          return email;
        }
      }
    }
  })
});
var ComplicatedArgs = new GraphQLObjectType({
  name: 'ComplicatedArgs',
  fields: () => ({ // as function
    complexArgField: {
      type: GraphQLString,
      args: {
        complexArg: { type: ComplexInput }
      },
    }
  }),
});
Alceste answered 19/9, 2016 at 18:1 Comment(1)
What difference in particular do you refer to? The first snippet creates a whole schema with a query type and a resolvable echo field, the second snippet only creates an object type with a complexArgField field and no resolver.Civility
A
6

When you need to make a circular reference.

Look for my similiar answer here

Dynamically creating graphql schema with circular references

Alexandraalexandre answered 19/9, 2016 at 18:7 Comment(2)
So would you say it is a good practice to always use fields as a function?Alceste
Thats strongly opinioned, but in my opinion it is! It has no big harm on the performanceAlexandraalexandre
T
6

This is a great example of CLOSURE. Imagine you have two types in a file and they are referencing each other.

const BookType= new GraphQLObjectType({
    name: 'BookType',
    fields: {  // as object
      author: {
        type: AuthorType,
        
        resolve: (parentValue, args) => {
          // query the author based on your db implementation.
        }
      } }
  })

BookType has a field author and referencing AuthorType. Now imagine you have AuthorType defined under "BookType" referencing BookType

const AuthorType= new GraphQLObjectType({
    name: 'AuthorType',
    fields: {  // as object
      books: {
        type: new GraphQLList(BookType), //one author might have multiple books
        
        resolve: (parentValue, args) => {
          // query the books based on your db implementation.
        }
      } }
  })

So when Javascript engine needs to use BookType it will see that fields.author.type is AuthorType and AuthorType is not defined above. So it will give

reference error:AuthorType is not defined

to circumvent this, we turn fields to a function. this function is a CLOSURE function. this is a great example why closures are so helpful.

When js engine reads the file first, it saves all the variables that are referenced inside of a function into memory heap as a closure storage of that function. All the variables that BookType.fields need are stored into the closure environment of the BookType.fields(). So now if javascript executes the Booktype.fields(), it checks if "AuthorType" is defined inside the function, it is not defined so it checks its closure storage, AuthorType was already stored there in the beginning, so it uses it.

Tweeddale answered 5/7, 2020 at 2:37 Comment(1)
great answer explaining JavaScript's hoisting behavior with closures.Plumlee

© 2022 - 2024 — McMap. All rights reserved.