Type-graphql: FieldResolver not work

Created on 30 Nov 2018  Â·  13Comments  Â·  Source: MichalLytek/type-graphql

index.ts:

import "reflect-metadata";
import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
import * as express from "express";
import * as graphqlHTTP from "express-graphql";
import expressPlayground from "graphql-playground-middleware-express";
import { buildSchema } from "type-graphql";
import { RecipeResolver } from "./recipe-resolver";
async function bootstrap() {
  const schema = await buildSchema({
    resolvers: [RecipeResolver],
  });

  const app = express();
  app.use(
    "/graphql",
    graphqlHTTP(async (req, res, params) => ({
      schema,
      validationRules: [
        queryComplexity({
          maximumComplexity: 20,
          variables: params!.variables!,
          onComplete: (complexity: number) => {
            console.log("Query Complexity:", complexity);
          },
          estimators: [
            fieldConfigEstimator(),
            simpleEstimator({
              defaultComplexity: 1,
            }),
          ],
        }),
      ],
    })),
  );
  app.get("/playground", expressPlayground({ endpoint: "/graphql" }));
  app.listen(4000, () => {
    console.log(
      `Server is running, GraphQL Playground available at http://localhost:4000/playground`,
    );
  });
}

bootstrap();

recipe-resolver.ts:

import { Resolver, Query, FieldResolver, Root, ResolverInterface, Arg } from "type-graphql";

import { Recipe } from "./recipe-type";
import { createRecipeSamples } from "./recipe-samples";

@Resolver(of => Recipe)
export class RecipeResolver{
  private readonly items: Recipe[] = createRecipeSamples();

  @Query(returns => [Recipe], {
    complexity: ({ childComplexity, args }) => args.count * childComplexity,
  })
  async recipes(@Arg("count") count: number): Promise<Recipe[]> {
    return await this.items.slice(0, count);
  }

  /* Complexity in field resolver overrides complexity of equivalent field type */
  @FieldResolver(returns => Number,{ complexity: 5 })
  ratingsCount(@Root() recipe: Recipe): number {
    return recipe.ratings.length;
  }
}

recipe-type.ts:

import { Field, ObjectType, Int, Float } from "type-graphql";

@ObjectType()
export class Recipe {
  /*
    By default, every field gets a complexity of 1.
  */
  @Field()
  title: string;

  /*
    Which can be customized by passing the complexity parameter
  */
  @Field(type => Int, { complexity: 2 })
  ratingsCount: number;

  @Field(type => Float, {
    nullable: true,
    complexity: 10,
  })
  get averageRating(): number | null {
    const ratingsCount = this.ratings.length;
    if (ratingsCount === 0) {
      return null;
    }
    const ratingsSum = this.ratings.reduce((a, b) => a + b, 0);
    return ratingsSum / ratingsCount;
  }

  // internal property, not exposed in schema
  ratings: number[];
}

recipe-samples.ts:

import { plainToClass } from "class-transformer";

import { Recipe } from "./recipe-type";

export function createRecipeSamples() {
  return plainToClass(Recipe, [
    {
      title: "Recipe 1",
      ratings: [0, 3, 1],
    },
    {
      title: "Recipe 2",
      ratings: [4, 2, 3, 1],
    },
    {
      title: "Recipe 3",
      ratings: [5, 4],
    },
  ]);
}

when i try to start the server, i got:

(node:7696) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'fields' of undefined
    at definitions.forEach.def (/home/ye/works/example/example_one/node_modules/type-graphql/metadata/metadata-storage.js:132:52)
    at Array.forEach (<anonymous>)
    at MetadataStorage.buildFieldResolverMetadata (/home/ye/works/example/example_one/node_modules/type-graphql/metadata/metadata-storage.js:122:21)
    at MetadataStorage.build (/home/ye/works/example/example_one/node_modules/type-graphql/metadata/metadata-storage.js:77:14)
    at Function.generateFromMetadataSync (/home/ye/works/example/example_one/node_modules/type-graphql/schema/schema-generator.js:26:51)
    at Function.<anonymous> (/home/ye/works/example/example_one/node_modules/type-graphql/schema/schema-generator.js:15:33)
    at Generator.next (<anonymous>)
    at /home/ye/works/example/example_one/node_modules/tslib/tslib.js:107:75
    at new Promise (<anonymous>)
    at Object.__awaiter (/home/ye/works/example/example_one/node_modules/tslib/tslib.js:103:16)
(node:7696) 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:7696) [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.

when i remove those code from recipe-resolver.ts:

   @FieldResolver(returns => Number,{ complexity: 5 })
  ratingsCount(@Root() recipe: Recipe): number {
    return recipe.ratings.length;
  }

it works..

why?

Question Solved

Most helpful comment

After running the code, it looks like the problem is here:
https://github.com/yeluojun/type-graphql-example-one/blob/master/tsconfig.json#L9

You need to have target: es2016 in your config to make it work:
https://github.com/19majkel94/type-graphql#typescript-configuration

All 13 comments

The code looks ok, the problem might be somewhere else 😕
Please create a repository with a minimal reproducible code example.

@19majkel94 thank you for your reply~ https://github.com/yeluojun/type-graphql-example-one

After running the code, it looks like the problem is here:
https://github.com/yeluojun/type-graphql-example-one/blob/master/tsconfig.json#L9

You need to have target: es2016 in your config to make it work:
https://github.com/19majkel94/type-graphql#typescript-configuration

@19majkel94 it works, thank you~

I have es2017 and still see the issue.

I'm getting the issue as well with all versions up to es2018.
sample repo.. https://gitlab.com/brian.campo/gql-ts-boilerplate2/commit/fc1a12cb5fe68467a21c2718e22fb57e9d210db4

Same problem as well. I'm using es2018

@jpagand
Could you create a repository with a minimal reproducible code example? 😉

@briancampo
You had ObjectType(); instead of @ObjectType() here:
https://gitlab.com/brian.campo/gql-ts-boilerplate2/blob/master/src/entity/User.ts#L5-7
Fixing this allows to create a schema without any issues.

@MichalLytek , Thanks much and apologies... I must have looked at it 100 times and missed it.
Loving the project!!

I'm having the same issue, here is a link to my repo

@nbouliol In Nest you have you use @ResolveProperty, not @FieldResolver.
The same goes to @Root or other decorators - only the one exported from @nestjs/graphql are allowed, the one from type-graphql can be used only to model object types or input types.

@MichalLytek Thanks for your resonse

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tongtwist picture tongtwist  Â·  3Comments

Janushan picture Janushan  Â·  3Comments

winuxue picture winuxue  Â·  4Comments

avkonst picture avkonst  Â·  3Comments

Tybot204 picture Tybot204  Â·  3Comments