Hello, I have the following schema:
@ObjectType()
class Action {
@Field(type => Skill, { nullable: true })
skill?: Skill;
}
@ObjectType()
export class ActionResult {
@Field(type => [Action])
actions!: Action[];
@Field({ nullable: true })
count?: number
}
@ObjectType()
export class Skill {}
@Resolver(Action)
export class ActionResolver extends BaseResolver {
@Query(returns => ActionResult)
async actions (@Args() args: ActionArgs, @Info() info){
}
@FieldResolver()
async skill(@Root() action: Action) {
}
}
@Resolver(Skill)
export class SkillResolver extends BaseResolver {
@Query(returns => [Skill])
async allSkills (){
}
}
From this I generate a schema like this:
return buildSchema({
resolvers: [ActionResolver, SkillResolver],
emitSchemaFile: { path: 'schema.graphql' },
})
And I get the error:
UnhandledPromiseRejectionWarning: Error: Schema must contain uniquely named types but contains multiple types named "Skill".
You can't extend a resolver's class without providing different queries/mutation names in a factory function or without making the base resolver isAbstract. This will result in duplicated queries/mutations in schema.
Hi, not sure what you mean, the BaseResolver class looks like this :
export default abstract class DefaultResolver {
async fetch(args, info) {
//logic
}
async fetchOne(id: string) {
return this.model.findOne({ _id: id });
}
async fetchAll() {
//logic
}
It does not have the @Resolver
How is this not ok ?
Oh, I see it's Skill type, not skill field from field resolver.
So please create a repository with a minimal reproducible code example.
BTW, it's better to inject a base service rather that extending a class to have access to common methods.
I would rather inject common methods, but then I would have to send the name of the model as a parameter, right now my BaseResolver has acess to the names of the model:
export default abstract class DefaultResolver {
abstract get name (): string;
abstract get plural (): string;
protected get model() {
return models[this.name];
}
async fetch(args, info) {
const {filter} = args;
const shouldCount = info.fieldNodes[0].selectionSet.selections.map(s => s.name.value).includes('count');
const filters = filterPipe(filter);
const data = await this.model.find(filters, null, skipLimitPipe(args));
const count = shouldCount ? await countModel(this.model, filters) : -1;
return {[this.plural]: data, count};
}
async fetchAll() {
return this.model.find();
}
async fetchOne(id: string) {
return this.model.findOne({ _id: id });
}
}
I found one way to produce this bug. Took me a long time to figure out! If you import a file from 2 other files but you type its path with 2 different casings, the TypeScript compiler will include 2 copies of the module in the transpiled JS. This causes any types to be registered multiple times. For example:
// recipe.ts
@ObjectType()
export class Recipe {
// ...
}
// file1.ts
import { Recipe } from './recipe';
console.log(Recipe); // Use it so the import doesn't get optimized away.
md5-8bff9907e23533a0547e35848d62b76e
// file2.ts
import { Recipe } from './Recipe'; // Notice the capital R
console.log(Recipe); // Use it again.
Error: Schema must contain uniquely named types but contains multiple types named "Recipe".
Of course, the import statements _should_ match the casing of the actual file. But the macOS filesystem is not case sensitive so it recognizes the file even when the import statement has the wrong casing. There is the compiler option forceConsistentCasingInFileNames that detects bugs like this. Strangely it's turned off by default.
@gotenxds Was this also the cause in your case?
Closing for a housekeeping purposes 馃敀
I'm struggling with this issue too. Would love to know how you solved it. Unfortunately @AdamDanielKing's solution didn't work, all import statements have consistent casing 馃槵
@jonlambert Would you be able to put together a minimal project that demonstrates the issue, so others can take a look?
Sure, I'll strip back the project I'm working on as soon as possible. It's a NestJS project, so let me know if you think the issue might be better raised there.
This was a hard one to track down, with an annoying fix (Nest specific, not the fault of type-graphql). For anyone experiencing the same issue make sure to delete dist/ directory and run the app again.
I have the same issue and I have it very bounded:
Object Type definition:
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity } from 'typeorm';
import { ObjectType, Field, ID } from 'type-graphql';
@ObjectType()
@Entity()
export class Point extends BaseEntity {
@Field(() => ID)
@PrimaryGeneratedColumn()
id!: number;
@Field()
@Column()
idMap!: number;
}
Resolver definition:
import { Point } from '../entities/Point';
import { Resolver, Query, Arg } from 'type-graphql';
import { Repository, getManager } from 'typeorm';
@Resolver()
export class PointResolver {
private pointService: Repository<Point>;
constructor() {
this.pointService = getManager().getRepository('Point');
}
@Query(() => [Point])
async pointsByIdMap(@Arg('idMap') idMap: number) {
const points = this.pointService.find({ where: { idMap } });
if (!points) {
return null;
}
return points;
}
}
So all of it works perfectly until I add the () => [Point] to the query. If instead of returning an array of Point, we return an array of Number for example (return [1,2]), it works fine. So maybe the call to the () => [Point] is interfering somehow?
The main tutorial I've been following to get to this issue is https://github.com/benawad/type-graphql-series but I don't find any reason for this problem.
Any idea?
I think you have a similar problem to mine and @jonlambert's. The reason it manifests when you add () => [Point] is that otherwise you're not using the Point import so TypeScript removes it as an optimization. I suspect you would also get the error if you simply added a console.log(Point).
Your code runs for me. Are you sure there isn't another file where you're importing Point with different casing in the filename, e.g. import { Point } from '../entities/point';?
I only have another file related with this, an InputType definition but I never use it since I'm trying to simplify the debugging:
import { Field, InputType } from 'type-graphql';
@InputType()
export class PointInput {
@Field()
idMap!: number;
}
When I search for any import or any .../entities/ in the project, only appear one result, the import in the resolver.
EDIT:
You were right, if I create another Query called version, and try to console.log(Point), throw the same error.
Here is the tests I've done:
ts-node src/index.ts which is the main file, it works perfectly: query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = current_schema() AND "table_name" = 'typeorm_metadata'
query: COMMIT
server started on http://localhost:4000/graphql
When I compile the TypeScript and try to execute the code with tsc-watch --onSuccess \"ts-node -r dotenv/config dist/index.js dotenv_config_path=./.env\" the same error appears again.
Also appears with ts-node /dist/index.js. So looks like is the compilation process which is interfering somehow.
Any idea with this new info? @MichalLytek @AdamDanielKing
Not sure. Would you be able to publish a minimal repo that reproduces the error?
Sure. I'm on it.
Here it is, @AdamDanielKing:
https://github.com/christopher-avila/type-graphql-and-typeorm-debug
@christopher-avila Your ormconfig.js is referencing the TS file for your entity:
You should either configure typeorm from a TS file or use the "dist/" path to the JS files:
entities: ["dist/entities/*.js"]
This fixes the problem. 馃檪
I can't believe it.
The simplest thing in the simplest file in the whole project.
Thank you so much for your time. Solved, right! Thank you!
Thank you @AdamDanielKing I have been struggling with a casing mismatch for the past couple of hours. Fixed the issue I was having.
For anyone who is struggling with this issue and tried above recipes with no luck: try to implicitly give a name to your types. My issue was solved with following
@ObjectType('user')
export class User {
Note an arg that was passed to ObjectType
For the record, I have this issue using Zeit now CLI tool. My GraphQL server is a lambda. First type it compiles, it works fine. When I change something in the code, it recompiles, and produce the error (Schema must contain uniquely named types but contains multiple types named...)
I'm still struggling to find a solution.
Looks like this is related with Webpack hot reload compatibility: https://github.com/MichalLytek/type-graphql/issues/289
@fromi
Do you use type-graphql@beta? It has fixed schema types leaks so now it should built the schema correctly, although it will still evaluate all decorators so there will be a memory leak on metadata storage side.
@MichalLytek I just tried, and it fixes my issue! Thanks 馃憤
I met the same issue with nestjs start:dev after i moved the entity file out from a subfolder.
and after 30 minutes, i finnally found that the reason is, the original entity file and the moved entity file are all in the dist folder. so multiple types were found by graphql.
then i simply deleted the dist folder and restart dev, all solved.
hope this helps
@vlad-elagin thank you a lot
@MichalLytek I just tried the beta and it worked. But how stable is it? Do you recommend using it?
@THPubs it's basically a RC, I was grouping breaking changes, soon v0.18 stable will be released 馃槈
@MichalLytek Cool thanks 馃榾
@fromi
Do you usetype-graphql@beta? It has fixed schema types leaks so now it should built the schema correctly, although it will still evaluate all decorators so there will be a memory leak on metadata storage side.
This solved it for me as well @MichalLytek FYI I am using Next.js which comes with hot reloading out of the box. The initial GraphQL query was always fine, but every other query resulted in this error too.
FYI: Whenever I used "DateTime" as input args, I got Cannot find module 'class-validator' Require stack for type-graphql@beta. I installed class-validator manually, then it worked, but then I got No metadata found. There is more than once class-validator version installed probably. You need to flatten your dependencies.. Not sure whether there is something missing in the latest beta release.
@rwieruch
class-validator is now a peer dependency, so it will use the same version as your app.
If you don't use the validation feature, just put validate: false in the buildSchema options.
Was using webpack to build, getting the same error on the generated bundle.
Fixed by putting ".mjs" in the extensions of webpack.config.js
{
...,
resolve: {
extensions: [".mjs", ".ts", ".tsx", ".js"]
}
}
No idea how mjs helps in this though.
For anyone who is struggling with this issue and tried above recipes with no luck: try to implicitly give a name to your types. My issue was solved with following
@ObjectType('user') export class User {Note an arg that was passed to ObjectType
yeah this is the real solution.
Complete solution in case if class is used as input and output:
@ObjectType('DataStruct')
@InputType('DataStructInput')
export class DataStruct { ... }
In my case I have created result model with decorater @ObjectType("result") for all models so due to conflit in different result model for each schema/resolver type-graphql schema generation pipeline giving error. So make sure fields, @ObjectType(), @InputType() should be unique
I fixed the issue by changing the paths.
import { User } from "./user.entity"; => import { User } from "../User/user.entity";
I had the same issue because I decorated the same class as @ObjectType and @InputType
@ObjectType()
@InputType()
export class ProductDto {
@Field() readonly id?: number;
@Field() readonly name: string;
}
The solution was to explicitly name the objects
@ObjectType('ProductDto')
@InputType('ProductDtoInput')
export class ProductDto {
@Field() readonly id?: number;
@Field() readonly name: string;
}
I'm getting this variation after deployment to Aws lambda:
{
"errorType": "Error",
"errorMessage": "Schema must contain uniquely named types but contains multiple types named \"h\".",
"stack": [
"Error: Schema must contain uniquely named types but contains multiple types named \"h\".",
" at new GraphQLSchema (/var/task/node_modules/graphql/type/schema.js:194:15)",
" at Function.generateFromMetadataSync (/var/task/node_modules/type-graphql/dist/schema/schema-generator.js:31:32)",
" at Object.buildSchemaSync (/var/task/node_modules/type-graphql/dist/utils/buildSchema.js:20:55)",
" at Object.t.default (/var/task/src/index.js:1:5251373)",
" at Object.80341 (/var/task/src/index.js:1:5271735)",
" at n (/var/task/src/index.js:1:5439183)",
" at /var/task/src/index.js:1:5439223",
" at Object.<anonymous> (/var/task/src/index.js:1:5439330)",
" at Module._compile (internal/modules/cjs/loader.js:999:30)",
" at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)"
]
}
I'm not sure where to start looking. Any hints?
Most helpful comment
This was a hard one to track down, with an annoying fix (Nest specific, not the fault of type-graphql). For anyone experiencing the same issue make sure to delete
dist/directory and run the app again.