MichalLytek / type-graphql

Create GraphQL schema and resolvers with TypeScript, using classes and decorators!
https://typegraphql.com
MIT License
8.03k stars 675 forks source link

Adding any @FieldResolver crashes build #630

Closed jaminhaber closed 4 years ago

jaminhaber commented 4 years ago

Describe the Bug Adding the @ Field decorator to a model does nothing and adding the @ FieldResolver decorator to a Resolver crashes the build process. Either this is a bug or I have done something wrong.

Adding any @ FieldResolver decorator will cause this, even one that simply returns a preset string.

To Reproduce

model

@ObjectType("Album")
export class AlbumModel {
  @Field(() => ID)
  id: string;

  @Field()
  title: string;

[1]
  // @Field(() => [SocialLinkModel])
  // social: SocialLinkModel[];

[2]
 // @Field(() => [SocialLinkModel])
 // social(@Root() album: AlbumEntity) {
 //   return album.social?.links ?? [];
 // }

}

@ObjectType("SocialLink")
export class SocialLinkModel {
  @Field(() => ID)
  id: string;

  @Field()
  site: string;

  @Field()
  url: string;
}

[3] In addition, doing this in place of [2] causes the same issue

 @Field(() => [SocialLinkModel])
  social(@Root() album: AlbumEntity): SocialLinkModel[] {
    return [{ id: "2", url: "foo", site: 1 }];
  }

resolvers

@Resolver((_of) => AlbumModel)
export class AlbumResolver {

[1]
  @FieldResolver(() => [SocialLinkModel])
  async social(@Root() album: AlbumEntity) {
    return album.social?.links ?? [];
  }

  @Query(() => [AlbumModel])
  albums() {
    return AlbumEntity.find({ relations : ["social","social.links"] });
  }
}

Expected Behavior [1]: Either the The schema should be able to build with the resolveProperty and work as expected. or [2] or [3]: The @Field should return the value in the return function.

Logs [1]

(node:76612) UnhandledPromiseRejectionWarning: Error: Unable to find type metadata for input type or object type named ''
    at /Users/me/documents/api/node_modules/type-graphql/dist/metadata/metadata-storage.js:183:27
    at Array.forEach (<anonymous>)
    at MetadataStorage.buildFieldResolverMetadata (/Users/me/documents/api/node_modules/type-graphql/dist/metadata/metadata-storage.js:167:21)
    at MetadataStorage.build (/Users/me/documents/api/node_modules/type-graphql/dist/metadata/metadata-storage.js:100:14)
    at Function.generateFromMetadataSync (/Users/me/documents/api/node_modules/type-graphql/dist/schema/schema-generator.js:27:51)
    at Function.generateFromMetadata (/Users/me/documents/api/node_modules/type-graphql/dist/schema/schema-generator.js:15:29)
    at Object.buildSchema (/Users/me/documents/api/node_modules/type-graphql/dist/utils/buildSchema.js:9:61)

[2] and [3] image

Environment (please complete the following information):

Additional Context Thanks for taking the time to look at this. Love this framework, would love to figure this out so I can continue using it!

jaminhaber commented 4 years ago

even this causes the same error

  @FieldResolver(() => String)
  foo() {
    return "bar";
  }

(error)

UnhandledPromiseRejectionWarning: Error: Unable to find type metadata for input type or object type named ''

I followed the stack trace to metadata/metadata-storage.js and logged the def object:

{
  kind: 'external',
  methodName: 'foo',
  schemaName: 'foo',
  target: [Function: AlbumResolver],
  getType: [Function: getType],
  typeOptions: {},
  complexity: undefined,
  description: undefined,
  deprecationReason: undefined,
  resolverClassMetadata: {
    target: [Function: AlbumResolver],
    getObjectType: [Function],
    isAbstract: false
  },
  params: [],
  roles: undefined,
  middlewares: [],
  directives: [],
  extensions: {},
  getObjectType: [Function]
}
MichalLytek commented 4 years ago

I bet you have target: es5 in your tsconfig.json file :stuck_out_tongue: https://typegraphql.com/docs/installation.html#typescript-configuration

jaminhaber commented 4 years ago

@MichalLytek it really be like that sometimes.

MichalLytek commented 4 years ago

@jaminhaber I know, many people like to play with code and forgot to read the docs first, and I have to handle all that kind of issues 😕

Does changing to es2018 fixed your issue?

jaminhaber commented 4 years ago

I changed to ES2020 and it works.

leobar37 commented 3 years ago

thanks

xinghul commented 3 years ago

I hit the same issue even with target: es2018. I have a similar set up as @jaminhaber, I logged out the def object:

{
  kind: 'external',
  methodName: 'user',
  schemaName: 'user',
  target: [Function: AdminManagerResolver],
  getType: [Function: getType],
  typeOptions: {},
  complexity: undefined,
  description: undefined,
  deprecationReason: undefined,
  resolverClassMetadata: {
    target: [Function: AdminManagerResolver],
    getObjectType: [Function (anonymous)],
    isAbstract: false
  },
  params: [
    {
      kind: 'root',
      target: [Function: AdminManagerResolver],
      methodName: 'user',
      index: 0,
      propertyName: undefined,
      getType: [Function: getType]
    }
  ],
  roles: undefined,
  middlewares: [],
  directives: [],
  extensions: {},
  getObjectType: [Function (anonymous)]
}

This is what my FieldResolver looks like:

@FieldResolver(() => User)
  public async user(@Root() manager: Manager): Promise<User> {
    return this.managerLoaders.user.load(manager.id);
  }

I made sure the tsconfig.json is correct based on the link provided above, any thoughts?

xinghul commented 3 years ago

One thing to mention is that I'm able to run the code just fine with ts-node or ts-node-dev.

However, when I was trying to build the source and run it with node, I got the error.

Here's how I am building the source with tsc and babel:

tsc && babel ./src --out-dir ./build --extensions '.ts,.js'

Not sure if it's babel not respecting the emitDecoratorMetadata? here's my babel.config.js:


module.exports = function(api) {
  api.cache(true);

  const presets = [
    '@babel/env',
    '@babel/preset-typescript',
  ];
  const plugins = [
    [
      "module-resolver",
      {
        "root": [
          "."
        ],
        "alias": {
          "@common": "./src/common",
          "@services": "./src/services",
          "@entities": "./src/entities",
          "@interfaces": "./src/interfaces",
          "@repositories": "./src/repositories",
          "@db": "./src/db",
          "@graphql": "./src/graphql"
        },
        "extensions": [
          ".js",
          ".ts"
        ]
      }
    ],
    'babel-plugin-transform-typescript-metadata',
    [
      '@babel/plugin-proposal-decorators',
      {
        legacy: true
      }
    ],
    [
      '@babel/plugin-proposal-class-properties',
      {
        loose: true
      }
    ],
    '@babel/proposal-object-rest-spread',
  ];

  return {
    presets,
    plugins,
  };
}
rades12340 commented 3 years ago

Does anyone have solutions to this problem? I'm having the same problem.

acro5piano commented 3 years ago

I think the babel plugin does not support decorator metadata. You can check transpiled code.

heeju commented 3 years ago

I have same problem. FileResolver doen't work with babel and babel-plugin-transform-typescript-metadata plugin

RyannGalea commented 3 years ago

I have the same issue here as @xinghul , I'm compiling with esbuild and a typescript plugin. I'm not sure how to resolve.

DroidGar commented 2 years ago

im having same problem, any update?

MrEmanuel commented 2 years ago

Weirdly enough, I solved this by setting "target": "ES6", in my tsconfig.json

{
  "extends": "@tsconfig/recommended/tsconfig.json",
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "lib": ["ES6", "esnext.asynciterable"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strictPropertyInitialization": false,
    "outDir": "./dist",
    "composite": true,
    "declaration": true,
    "declarationMap": true
  }
}
karmatradeDev commented 2 years ago

hi im getting this error Error: Unable to find type metadata for input type or object type named ''

I followed the install instructions

"reflect-metadata": "^0.1.13",
"type-graphql": "1.2.0-rc.1"

am I using the wrong version

{ "compilerOptions": { "target": "es2020", "lib": ["dom", "dom.iterable", "es2020", "esnext.asynciterable"], "allowJs": true, "skipLibCheck": true, "strict": false, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "strictPropertyInitialization": false, "composite": true, "declaration": true, "declarationMap": true }, "include": ["next-env.d.ts", "*/.ts", "*/.tsx"], "exclude": ["node_modules" ] }

@FieldResolver() async link(@Root() parent: Vibe, @Ctx() ctx: Context) { return 'test' }

ContrerasA commented 2 years ago

In my case, this was resolved from switching the class's decorator from @Object to @ObjectType

clintonb commented 1 year ago

If you are using swc, make sure it is configured to output ES2018 or later:

{
  "$schema": "https://json.schemastore.org/swcrc",
  "jsc": {
    "target": "es2020",  // << ---
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": true,
      "dynamicImport": false
    },
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    }
  },
  "module": {
    "type": "commonjs"
  }
}