graphql icon indicating copy to clipboard operation
graphql copied to clipboard

GraphQl Multiple Endpoints not working

Open arslanmughal99 opened this issue 4 years ago • 35 comments

Bug Report

Current behavior

When adding multiple endpoints for graphql it gives following weird error

(node:16316) UnhandledPromiseRejectionWarning: Error: Schema must contain uniquely named types but contains multiple types named "User".
    at typeMapReducer (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\graphql\type\schema.js:262:13)
    at Array.reduce (<anonymous>)
    at new GraphQLSchema (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\graphql\type\schema.js:145:28)
    at GraphQLSchemaFactory.<anonymous> (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\@nestjs\graphql\dist\schema-builder\graphql-schema.factory.js:28:28)
    at Generator.next (<anonymous>)
    at C:\Users\arsla\Documents\nest-graphql-practice\node_modules\tslib\tslib.js:113:75
    at new Promise (<anonymous>)
    at Object.__awaiter (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\tslib\tslib.js:109:16)
    at GraphQLSchemaFactory.create (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\@nestjs\graphql\dist\schema-builder\graphql-schema.factory.js:23:24)
    at GraphQLSchemaBuilder.<anonymous> (C:\Users\arsla\Documents\nest-graphql-practice\node_modules\@nestjs\graphql\dist\graphql-schema.builder.js:55:56)
(node:16316) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:16316) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Input Code

My appModule look the following

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { MessagesModule } from './messages/messages.module';
import { UserModule } from './user/user.module';

@Module({
  imports: [
    MessagesModule,
    UserModule,
    
    GraphQLModule.forRoot({
      autoSchemaFile: 'userSchema.gql',
      playground: true,
      include: [UserModule],
      path: '/user',
    }),

    GraphQLModule.forRoot({
      autoSchemaFile: 'messageSchema.gql',
      playground: true,
      include: [MessagesModule],
      path: '/msg',
    })
  ],
})
export class AppModule { }

Expected behavior

The error above is totally unexpected , I have taken code first approach Also i have tried a lot but didn't find help any where.

Possible Solution

Environment


Nest version: X.Y.Z

 
For Tooling issues:
- Node version: v12.6.0
- Platform:  Windows

Others:
 "@nestjs/common": "^7.0.0",
    "@nestjs/core": "^7.0.0",
    "@nestjs/graphql": "^7.1.3",
    "@nestjs/platform-express": "^7.0.0",
    "apollo-server-express": "^2.11.0",
    "graphql": "^14.6.0",
    "graphql-tools": "^4.0.7",

arslanmughal99 avatar Mar 30 '20 09:03 arslanmughal99

Please, provide a minimal repository which reproduces your issue.

kamilmysliwiec avatar Mar 30 '20 09:03 kamilmysliwiec

@kamilmysliwiec here is the repository to reproduce nestjs-graphql-multiple-endpoint-issue if you disable any one of the end point it will work fine but raising error when both are activated at once

arslanmughal99 avatar Mar 30 '20 09:03 arslanmughal99

Any updates ? i have tried "schema First approach" with different schemas but no luck so far :(

arslanmughal99 avatar Mar 31 '20 18:03 arslanmughal99

So looks like something wrong with graphql dependency i have commented this line in node_modules\graphql\type\schema.js line 264

 if (seenType) {
    if (seenType !== namedType) {
      console.log(seenType, namedType);
      console.log(typeof seenType, typeof namedType);
      // throw new Error("Schema must contain uniquely named types but contains multiple types named \"".concat(namedType.name, "\"."));
    }

Now all working perfectly . but this not look like a permanent solution i have tried to console seenType and namedType both name user and typeof object. any perment solution ?

arslanmughal99 avatar Apr 01 '20 10:04 arslanmughal99

The issue is that using code-first approach means using TS metadata because of all the decorators. TypeScript is unaware of the NestJS modules e.g. it cannot know that a @Resolver class belongs to a certain module thus the metadata storage stores ALL resolvers and makes them available to the global scope. Both GraphQLModules then load this metadata storage and both see the same resolvers and within the resolvers you're using the types which are shared again and so on thus resulting in duplicate types. I also do not know a solution for this yet, would appreciate it.

Ponjimon avatar May 18 '20 13:05 Ponjimon

@lookapanda Hi ...! I have just solve this issue by using schema first approach . Because i got no luck after in code-first approach .

arslanmughal99 avatar May 20 '20 14:05 arslanmughal99

Hi @arslanmughal5566 ,

can you provide full code example of root module please.

I am trying to create one GraphQLModule with autoSchemaFile and second GraphQLModule without this flag.

JaLe29 avatar May 21 '20 07:05 JaLe29

@JaLe29 Hello my solution was this app.module.ts

@Module({
  imports: [
  GraphQLModule.forRoot({
    debug: true,
    path: 'user',
    playground: true,
    include: [UserModule],
    typePaths: ['src/user/**/*.gql'],
    context: ({ req, res }) => ({ req, res })
  }),
  GraphQLModule.forRoot({
    debug: true,
    path: 'auth',
    playground: true,
    include: [AuthModule],
    typePaths: ['src/auth/**/*.gql'],
    context: ({ req, res }) => ({ req, res })
  })
],
 providers: [],
 exports: []
})

export class AppModule { }

arslanmughal99 avatar May 21 '20 14:05 arslanmughal99

Any updates ? i have tried "code First approach" with different schemas but no luck so far :(

I've created also an example, see #896

kaufmo avatar May 25 '20 14:05 kaufmo

@kaufmo Your issue is this https://github.com/kaufmo/nestjs-typeorm-multiple-graphql-bug/blob/684bab8815a001d4be8c9da00c98b7c36590d0d2/src/app.module.ts#L28

Dont use autoSchema . Remove code-first approch totally from the app and create schema.gql manually and then give it path to typePath like this


@Module({
  imports: [
  GraphQLModule.forRoot({
    debug: true,
    path: 'user',
    playground: true,
    include: [AdminModule],
    typePaths: ['your/path/to/*.gql'],
    context: ({ req, res }) => ({ req, res })
  }),
  GraphQLModule.forRoot({
    debug: true,
    path: 'auth',
    playground: true,
    include: [FrontendModule],
    typePaths: ['your/path/to/*.gql'],
    context: ({ req, res }) => ({ req, res })
  })
],
 providers: [],
 exports: []
})

export class AppModule { }

arslanmughal99 avatar May 25 '20 17:05 arslanmughal99

Will do my best to fix this issue as soon as possible

kamilmysliwiec avatar May 25 '20 18:05 kamilmysliwiec

@kamilmysliwiec thx that would be really nice if its working. My existing api contains about 50 modules with code first approach, I don't want to update these to schema first.

kaufmo avatar May 26 '20 05:05 kaufmo

@kamilmysliwiec do you have any information for me when this will be fixed?

kaufmo avatar Jun 10 '20 06:06 kaufmo

@kamilmysliwiec Have any update for this issue?

TienHuong avatar Jul 09 '20 03:07 TienHuong

@kamilmysliwiec Looking for the fix. Any update?

bhuvan0616 avatar Jul 30 '20 06:07 bhuvan0616

This is also not working for me -- same error as OP. Is the solution known here? I'd be happy to work on a PR if somebody can point me in the right direction. Something about the typescript metadata to graphql type generator is duplicating types when more than 1 endpoint is configured?

jshearer avatar Sep 11 '20 19:09 jshearer

a solution to this problem would be fantastic!

grinono avatar Sep 29 '20 09:09 grinono

I want to point out that this issue is related to #1171. I have a mono-repo with shared models. The models directly are decorated with @nextjs/graphql metadata. Simply requiring my shared models library automatically includes all types even though I have a resolver only using one type.

j avatar Sep 30 '20 19:09 j

I'm also having this issue and I really need to get this working. Is there anything that I could help with to get this resolved?

NormySan avatar Oct 15 '20 13:10 NormySan

@NormySan I found that when I changed my annotations from @ObjectType() to @ObjectType({ isAbstract: true }) it fixed the issue for me. Not sure if this helps you but thought I'd share.

brycedeneen avatar Oct 18 '20 22:10 brycedeneen

@brycedeneen Thanks for the info. I'll give it a try as a temporary solution.

NormySan avatar Oct 19 '20 06:10 NormySan

Has anybody investigated which part of the code that might be causing this issue? I'm going to try to solve it if I get some spare so if anybody has any input as to what could be causing it this would be a huge help to getting started!

NormySan avatar Oct 22 '20 15:10 NormySan

@NormySan I found that when I changed my annotations from @ObjectType() to @ObjectType({ isAbstract: true }) it fixed the issue for me. Not sure if this helps you but thought I'd share.

Use isAbstract: true in every ObjectType is the only approach that makes sense. I can't imagine why someone would want to include an unused (even registered) ObjectType in generated schema when using code first approach, because its main advantage is to keep consistency. I think isAbstract: true should be the default.

francisfontoura avatar Oct 25 '20 00:10 francisfontoura

@francisfontoura According to the documentation isAbstract is used to suppress SDL generation for the specific object type. If this solves the problem while it's still added to the schema then it's most likely a bug since thats not the expected behaviour from my understanding.

I'm not trying to include unused object types in my schema, I'm building an application with multiple GraphQL endpoints and both endpoints are getting the same object types, resolvers etc. added do them even though they are completely seperated and it's configured to only load object types, resolvers etc. from specific modules.

NormySan avatar Oct 25 '20 07:10 NormySan

I tried the isAbstract: true method, but this still doesn't work the moment I use any enum, where I can't set this property. Is this even in progress? Since this is opened since March and it's still not fixed, maybe it would be a good idea to remove it from the docs as it seems it's actually supported?

vnenkpet avatar Nov 06 '20 10:11 vnenkpet

Any solution? I followed the "isAbstract: true" solution, but my scalar types are throwing a multiple types error.

maximseshuk avatar Nov 28 '20 04:11 maximseshuk

I decided to switch my code from code first to schema first and now everything works as it should.

NormySan avatar Nov 30 '20 14:11 NormySan

Any chance someone has a solution to solve that while keeping code-first approach ?

TBG-FR avatar Jan 07 '21 10:01 TBG-FR

After some trials and errors I came up with working solution/workaround that allows to keep the code-first approach while having multiple GraphQL endpoints. The solution uses the NestJS workspaces.

The trick is in splitting the project into multiple apps with a shared library. Each of these apps has its own GraphQL configuration and can use whatever models and resolvers from the shared library. As both apps are built separately the schema is generated without any naming conflicts.

Here is the repo with the example

altick avatar Jan 19 '21 11:01 altick

Even one endpoint

GraphQLModule.forRoot({
      typePaths: ['./src/**/*.graphql'],
      context: ({ req }) => ({ authToken: req.headers.authorization }),
      resolvers: {
        DateTime: GraphQLDateTime,
        Upload: GraphQLUpload,
      },

is not generating schema at launching tried many paths /src src ../src

ahmedyounes avatar Jan 29 '21 07:01 ahmedyounes