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?
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
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: es2016in your config to make it work:https://github.com/19majkel94/type-graphql#typescript-configuration