Search code examples
typescriptgraphqlapollo

Graphql Schema type error indicating duplicate types, which it does not have?


I have now completely wasted my evening trying to work this one out but am beyond an answer, even after multiple searches.

I have my server which grabs my folders of '.graphql' files and merges them into one large one which I pass along with my resolvers into 'makeExecutableSchema()' as seen below

        const folders      = fs.readdirSync(path.join(__dirname, './api'));
        const allResolvers = [];
        let typeDefs       = '';

        folders.forEach(folder => {
          if (folder !== 'directive') {
            typeDefs = typeDefs.concat(requireText(`./api/${folder}/${folder}.graphql`, require));
            const { resolver } = require(`./api/${folder}/${folder}.resolvers`);
            allResolvers.push(resolver);
          }
        });

        const resolvers = _.merge(allResolvers);
        return makeExecutableSchema({typeDefs, resolvers});

Now i am constantly getting an error

    Error: Schema must contain unique named types but contains multiple types named "Address".
        at invariant (C:\100hotwater\src\server\node_modules\graphql\jsutils\invariant.js:19:11)
        at typeMapReducer (C:\100hotwater\src\server\node_modules\graphql\type\schema.js:216:58)
        at Array.reduce (<anonymous>)
        at C:\100hotwater\src\server\node_modules\graphql\type\schema.js:237:36
        at Array.forEach (<anonymous>)
        at typeMapReducer (C:\100hotwater\src\server\node_modules\graphql\type\schema.js:232:51)
        at Array.reduce (<anonymous>)
        at new GraphQLSchema (C:\100hotwater\src\server\node_modules\graphql\type\schema.js:122:28)
        at Object.extendSchema (C:\100hotwater\src\server\node_modules\graphql\utilities\extendSchema.js:161:10)
        at buildSchemaFromTypeDefinitions (C:\100hotwater\src\server\node_modules\graphql-tools\dist\schemaGenerator.js:186:28)
        at _generateSchema (C:\100hotwater\src\server\node_modules\graphql-tools\dist\schemaGenerator.js:97:18)
        at Object.makeExecutableSchema (C:\100hotwater\src\server\node_modules\graphql-tools\dist\schemaGenerator.js:110:20)
        at Object.exports.allSchema (C:\100hotwater\src\server\helpers.js:22:28)
        at Server.graphQl (C:\100hotwater\src\server\index.js:28:34)
        at new Server (C:\100hotwater\src\server\index.js:17:14)
        at Object.<anonymous> (C:\100hotwater\src\server\index.js:42:19)

This schema most definitely does not contain duplicates though as seen below of it printed out right before entering the "makeExecutableSchema"()" function.

      type Lead {
        _id: String
        owner: User
        address: Address
        steps: [Step]
      }

      type Mutation {
        createLead(
          owner: String!
          address: Address
          steps: [Step]
        ): Lead
      }

      type Address {
        lat: Int
        lng: Int
        formattedAddress: String
      }

      type Step {
        goto: String
        key: String
        str: String
      }

      type User {
        _id: String
        email: String
        firstName: String
        lastName: String
        mobile: Int
        phone: Int
        billingAddress: String
        password: String
        leads: [Lead]
      }

      type Query {
        signedInUser: User
        userByEmail(email: String!): User # @isAuthenticated
      }

      extend type Mutation {
        signIn(email: String!, password: String!): SignInResponse!
        createUser(
          email: String!
          firstName: String!
          lastName: String
          mobile: Int
          billingAddress: String
          password: String
        ): User
      }

      type SignInResponse {
        token: String
        refreshToken: String
      }

I noticed in the file "node_modules\graphql\type\schema.js:216:58" it seems to loop through the typings twice and obviously catching the Address twice in the check and posting the error but i'm still unsure where to go from here. Simple errors like this in Graphql is making it really hard to love.

Any help is appreciated.

The Fix

In short thanks to Dan below, I was defining the Address type and using it for both my Input (Mutation) and my Output (Query - Lead). What I didn't realise at the time was that you needed to define types & inputs differently. The working .graphql schema below.

      type Lead {
        _id: String
        owner: User
        address: Address
        steps: [Step]
      }

      type Address {
        lat: Int
        lng: Int
        formattedAddress: String
      }

      type Mutation {
        createLead(
          owner: String!
          address: AddressInput 
          steps: [Step]
        ): Lead
      }

      input AddressInput {
        lat: Int
        lng: Int
        formattedAddress: String
      }

Solution

  • I'm not sure what one of your resolver modules looks like, but I suspect you meant to do something like this:

    const resolvers = _.merge(...allResolvers) // note the spread operator
    

    Otherwise, calling merge is not actually doing anything and your resolvers property ends up being an array instead of an object.

    EDIT: Didn't notice it on first glace, but you're using types as inputs. If you want to use an object with the same shape as the Address type, you have to define a separate input type for it:

    type Address {
      lat: Int
      lng: Int
      formattedAddress: String
    }
    
    input AddressInput {
      lat: Int
      lng: Int
      formattedAddress: String
    }
    

    Types cannot be used for inputs and inputs cannot be used for types. If we were defining the schema programatically, we'd normally see an error to that effect. Evidently generating the schema from SDL doesn't generate the same error. If you add the appropriate input types for Address and Step, your schema should generate correctly.