Type-graphql: Does mutation requires all inputs in a specific field (e.g "input")?

Created on 23 Mar 2018  路  7Comments  路  Source: MichalLytek/type-graphql

The mutation examples show that the inputs have to be put under a specific field e.g (@Arg("input")). When I use @Args() instead, I'm getting this error when the server starts

(node:15051) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'target' of undefined at params.reduce (/web/node_modules/type-graphql/schema/schema-generator.js:240:69)
Is it a must to use @Arg("input")?

This example won't work:
@Mutation(returnType => ItemLike) async updateItemLikes(@Args() {id, userId}: UpdateItemLikesArgs) { return .... }

Question Solved

Most helpful comment

Is InputType() different from ArgsType()?

Of course! InputType will generate real GraphQLInputType and should be used when you want to have nested object in args:

@InputType()
export class UpdateItemLikeArgs {
    @IsInt()
    @Field(type => Int)
    id: number;

    @IsInt()
    @Field(type => Int)
    userId: number;
}
class Resolver {
    @Mutation(returnType => ItemLike)
    async updateItemLikes(@Arg("payload") {id, userId}: UpdateItemLikesArgs,@Ctx() context:any) {}
}

will generate:

input UpdateItemLikesArgs {
  id: Int!
  userId: Int!
}
type Mutation {
  updateItemLikes(payload: UpdateItemLikesArgs!): ItemLike!
}

But ArgsType is virtual, it will be flattened in schema:

@ArgsType()
export class UpdateItemLikeArgs {
    @IsInt()
    @Field(type => Int)
    id: number;

    @IsInt()
    @Field(type => Int)
    userId: number;
}
class Resolver {
    @Mutation(returnType => ItemLike)
    async updateItemLikes(@Args() {id, userId}: UpdateItemLikesArgs,@Ctx() context:any) {}
}

So it will look like this in SDL:

type Mutation {
  updateItemLikes(id: Int!, userId: Int!): ItemLike!
}

So you can't use @Args() directly with input type, it can be nested as a field of ArgsType. To mount input field directly to mutation, use @Arg("name") decorator.

Is that clear enough now? 馃槈

All 7 comments

I use es2015. This is my whole config file:

{ "compileOnSave": true, "compilerOptions": { "module": "commonjs", "target": "es2015", "rootDir":"./type_src", "moduleResolution": "node", "removeComments": false, "allowSyntheticDefaultImports": true, "noImplicitAny": false, "sourceMap": true, "outDir": "./src", "experimentalDecorators": true, "noLib": false, "declaration": false, "emitDecoratorMetadata": true, "lib": ["es6", "dom"], "types": ["reflect-metadata","system"], "jsx": "react", "inlineSources":true, "skipLibCheck": true }, "exclude": [ "node_modules" ], }
Typescript:
@Mutation(returnType => ItemLike) async updateItemLikes(@Args() {id, userId}: UpdateItemLikesArgs,@Ctx() context:any) {}

This is the generated javascript of the applied decorator:

````
class Reolver{
updateItemLikes({ mid, userId }, context) {

}

}

__decorate([
type_graphql_1.Mutation(returnType => ItemLike_1.ItemLike),
__param(0, type_graphql_1.Args()), __param(1, type_graphql_1.Ctx()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [UpdateItemLikeArgs, Object]),
__metadata("design:returntype", Promise)
], ItemLikeResolver.prototype, "updateItemLikes", null);
````

Does the decorator use class-transformer?

The problem is here:

const argumentType = metadata_storage_1.MetadataStorage.argumentTypes.find(it => it.target === param.getType());
let superClass = Object.getPrototypeOf(argumentType.target);

Looks like UpdateItemLikesArgs has not been found in MetadataStorage.argumentTypes - have you decorated the class with @ArgsType decorator?

@19majkel94
Yes, using ArgsType() instead of InputType() fixes the error.
Is InputType() different from ArgsType()? The doc only shows the use of @InputType() for mutaton
https://github.com/19majkel94/type-graphql/blob/master/docs/validation.md
````
@ArgsType
//@InputType()
export class UpdateItemLikeArgs {
@IsInt()
@Field(type => Int)
id: number;

@IsInt()
@Field(type => Int)
userId: number;

}
````

Is InputType() different from ArgsType()?

Of course! InputType will generate real GraphQLInputType and should be used when you want to have nested object in args:

@InputType()
export class UpdateItemLikeArgs {
    @IsInt()
    @Field(type => Int)
    id: number;

    @IsInt()
    @Field(type => Int)
    userId: number;
}
class Resolver {
    @Mutation(returnType => ItemLike)
    async updateItemLikes(@Arg("payload") {id, userId}: UpdateItemLikesArgs,@Ctx() context:any) {}
}

will generate:

input UpdateItemLikesArgs {
  id: Int!
  userId: Int!
}
type Mutation {
  updateItemLikes(payload: UpdateItemLikesArgs!): ItemLike!
}

But ArgsType is virtual, it will be flattened in schema:

@ArgsType()
export class UpdateItemLikeArgs {
    @IsInt()
    @Field(type => Int)
    id: number;

    @IsInt()
    @Field(type => Int)
    userId: number;
}
class Resolver {
    @Mutation(returnType => ItemLike)
    async updateItemLikes(@Args() {id, userId}: UpdateItemLikesArgs,@Ctx() context:any) {}
}

So it will look like this in SDL:

type Mutation {
  updateItemLikes(id: Int!, userId: Int!): ItemLike!
}

So you can't use @Args() directly with input type, it can be nested as a field of ArgsType. To mount input field directly to mutation, use @Arg("name") decorator.

Is that clear enough now? 馃槈

Just ran into this myself - might be worth checking when @Args() is used, and providing a helpful error message explaining (or even just linking to this issue thread).

It's described in FAQ:
https://19majkel94.github.io/type-graphql/docs/faq.html#is-inputtype-different-from-argstype

So if the problem is that people use @Args with @InputType or without @ArgsType, I can check and handle that 馃槈

Was this page helpful?
0 / 5 - 0 ratings

Related issues

robertchung97 picture robertchung97  路  3Comments

tongtwist picture tongtwist  路  3Comments

laukaichung picture laukaichung  路  3Comments

memark picture memark  路  3Comments

MichalLytek picture MichalLytek  路  4Comments