graphql
graphql copied to clipboard
GraphQl Multiple Endpoints not working
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",
Please, provide a minimal repository which reproduces your issue.
@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
Any updates ? i have tried "schema First approach" with different schemas but no luck so far :(
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 ?
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.
@lookapanda Hi ...! I have just solve this issue by using schema first approach . Because i got no luck after in code-first approach .
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 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 { }
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 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 { }
Will do my best to fix this issue as soon as possible
@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.
@kamilmysliwiec do you have any information for me when this will be fixed?
@kamilmysliwiec Have any update for this issue?
@kamilmysliwiec Looking for the fix. Any update?
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?
a solution to this problem would be fantastic!
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.
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 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 Thanks for the info. I'll give it a try as a temporary solution.
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 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 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.
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?
Any solution? I followed the "isAbstract: true" solution, but my scalar types are throwing a multiple types error.
I decided to switch my code from code first to schema first and now everything works as it should.
Any chance someone has a solution to solve that while keeping code-first approach ?
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
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