1

I am working on my first GraphQL schema. I've never done this before, and I am confused by the errors that I get. For instance:

  /Users/lorm/projects/aggregated_centralized_api/node_modules/graphql/jsutils/invariant.js:20
      throw new Error(message);
      ^

  Error: Can only create NonNull of a Nullable GraphQLType but got: function GraphQLList(type) {
      _classCallCheck(this, GraphQLList);

      (0, _jsutilsInvariant2['default'])(isType(type), 'Can only create List of a GraphQLType but got: ' + type + '.');
      this.ofType = type;
    }.
      at invariant (/Users/lorm/projects/aggregated_centralized_api/node_modules/graphql/jsutils/invariant.js:20:11)
      at new GraphQLNonNull (/Users/lorm/projects/aggregated_centralized_api/node_modules/graphql/type/definition.js:761:39)
      at Object.<anonymous> (/Users/lorm/projects/aggregated_centralized_api/server/schema.js:121:19)
      at Module._compile (module.js:413:34)
      at normalLoader (/Users/lorm/projects/aggregated_centralized_api/node_modules/babel-core/lib/api/register/node.js:199:5)
      at Object.require.extensions.(anonymous function) [as .js] (/Users/lorm/projects/aggregated_centralized_api/node_modules/babel-core/lib/api/register/node.js:216:7)
      at Module.load (module.js:357:32)
      at Function.Module._load (module.js:314:12)
      at Module.require (module.js:367:17)
      at require (internal/module.js:16:19)

My schemaTypes.js file looks like this:

  export const BookType = new GraphQLObjectType({
      name: 'Book',
      description: 'A Book',
      fields: () => ({
    book_id: {
        type: GraphQLString,
        description: 'UUID for book',
    },
    title: {
        type: GraphQLString,
        description: 'The name of the book',
    },
    author: {
        type: GraphQLList,
        description: 'List of authors, each author an object',
    },
    isbn: {
        type: GraphQLString,
        description: 'The primary way of identifying the book both domestically and internationally',
    },
    agency_price: {
        type: GraphQLFloat,
        description: 'The price set by the agency',
    },
    pub_date: {
        type: GraphQLString,
        description: 'The publication date of the book',
    },
    amazon_rank: {
        type: GraphQLInt,
        description: "The book's current selling rank on Amazon, updated daily",
    },
    reviews: {
        type: GraphQLFloat,
        description: "The book's current average review rating on Amazon, can 1 to 5, accurate to 1 decimal, updated daily",
    },
    book_img: {
        type: GraphQLString,
        description: 'Absolute URL for the image used for the book cover',
    },
    asin: {
        type: GraphQLString,
        description: "Amazon Standard Identification Numbers (ASINs) are unique blocks of 10 letters and/or numbers that identify items",
    },
    publisher: {
        type: GraphQLString,
        description: "The publisher name. There is only one publisher per book.",
    },
    bisacs: {
        type: GraphQLList,
        description: 'A list of Book Industry Standards and Communications subject headings as strings',
    },
    series_name: {
        type: GraphQLString,
        description: "If the book belongs to a series, the name of the series",
    },
    volume: {
        type: GraphQLString,
        description: "If the book is part of a series, this is its location in the sequence",
    },
    formats: {
        type: GraphQLList,
        description: 'Open Road has 20 standard formats',
    },
    keywords: {
        type: GraphQLList,
        description: 'Open Road puts mulitiple keywords (separated by a semi-colon) into the keyword1 field in Firebrand',
    },
    description: {
        type: GraphQLString,
        description: "Open Road's summary of the book",
    },
    campaigns: {
        type: GraphQLList,
        description: "A list full of marketing Campaigns we've done for the author",
    },
    retailers: {
        type: GraphQLList,
        description: 'A list full of Retailers, holding data specific to this book',
    },
    nominations: {
        type: GraphQLList,
        description: 'A list full of nominations to which this book belongs',
    },
    created: {
        type: GraphQLInt,
        description: 'The creation timestamp of this book'
    }
      })
  });

And by schema.js looks, in part, like this:

  let schema = new GraphQLSchema({
    query: new GraphQLObjectType({
      name: 'RootQueryType',
      fields: {
        book: {
          type: new GraphQLList(BookType),
          resolve: () => ""
        },
        book_retailer_summary: {
          type: BookRetailerSummaryType,
          args: {
            name: {
              description: 'Summary of books sales information, via various retailers',
              type: new GraphQLNonNull(GraphQLString)
            }
          },
          resolve: (root, {name}) => {
            return mongo()
              .then(db => {
                let deferred = Q.defer();

                let collection = db.collection('users');
                collection.find({ name })
                  .toArray((err, docs) => {
                    if (err) {
                      deferred.reject(err);
                      return;
                    }

                    db.close();
                    deferred.resolve(docs.length ? docs[0] : null);
                  });

                return deferred.promise;
              });

          }
        }
      }
    }),

So I am curious what mistake I am making?

[[ UPDATE ]]

Responding to helfer's remarks I have to wonder about this use of GraphQLList:

  nomination: {
    type: NominationType,
    args: {
      name: {
        description: 'The name of the list of books',
        type: new GraphQLNonNull(GraphQLString)
      },
      books: {
        description: 'The name of the books belonging to this nomination',
        type: new GraphQLNonNull(GraphQLList)
      }
    },

Perhaps this line is not allowed?

        type: new GraphQLNonNull(GraphQLList)

Is there some way I can get better error messages, so I can narrow in on what the real problem is?

1 Answer 1

1

GraphQLList and GraphQLNonNull are wrapping types, that means they always have to be created as follows:

const someListType = new GraphQLList(anotherType);
const someNonNullType = new GraphQLNonNull(anotherType);

Your book type has many fields that have the type GraphQLListType without actually creating an instance (i.e. without calling new and passing the type to wrap). The error message indicates that you're trying to wrap a GraphQLListType in NonNull somewhere, but it doesn't work because you didn't create the list type properly.

By the way, the schemas can get quite long and hard to read in Javascript, which is why I've created a package that lets you write them in schema language so it's much easier to read:

type Book {
  book_id: String
  title: String
  author: [Author]
  # etc.
}

You can find the documentation here: http://docs.apollostack.com/apollo-server/generate-schema.html

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

5 Comments

helfer, thank you. I am currently feeling overwhelmed trying to learn the Javascript version, yet I feel I should understand it before I learn yet another language. But I admire the effort you've made to simplify things.
I've added a follow up quesiton about this line: type: new GraphQLNonNull(GraphQLList)
I tried to change this to: "type: new GraphQLList(BookType)" but got "Book.author field type must be Output Type but got: function GraphQLList(type) { _classCallCheck(this, GraphQLList);" Does this mean I need to full specify every field in the book? Do I have to pass in an already initialized book, with all of its fields filled out?
@lorm Yes, the line you posted is indeed invalid. The Book type doesn't have to be initialized yet, if you pass a so-called "thunk" to create fields for types that have circular references, which you've already done for Book. I don't know of any ways to get better error messages for this kind of thing.

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.