Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use the same class as Input and Object type in GraphQL in NestJS

I am trying to setup my graphql resover to handle an array of objects but cant get the @Args decorator configured.

I created my own ArgsType

import { ArgsType, Field, Int, ObjectType } from '@nestjs/graphql';

@ArgsType()  // to be used as type in the resolver
@ObjectType()  // for schema generation 
export class Club {
 @Field(type => String)
 president: string;

 @Field(type => Int)
 members?: number;
}

Resolver with adding a single Club works just fine!

 @Query(() => Int)
 async addClub(@Args() club: Club) {
  // handle stuff
  }

but if I want to give an array of Club like this

 @Query(() => Int)
   async addClubs(@Args({name: 'clubs', type: () => [Club]}) clubs: Array<Club>) {
   // handle stuff
  }

this thows an error when nest is starting up

 UnhandledPromiseRejectionWarning: Error: Cannot determine a GraphQL input type for the "clubs". Make sure your class is decorated with an appropriate decorator.

although I am able to use an array of Strings like this

 @Query(() => [String])
 async addStrings(@Args({ name: 'clubs', type: () => [String], }) clubs: Array<string>) {
  // handle stuff
  }

I am pretty sure there should be an easy solution, but cant figure out where to go from here.

like image 525
FuzzyTemper Avatar asked Oct 30 '20 11:10

FuzzyTemper


People also ask

Can type be used for input GraphQL?

What is the input type? The input type in a GraphQL schema is a special object type that groups a set of arguments together, and can then be used as an argument to another field. Using input types helps us group and understand arguments, especially for mutations.

What is a resolver in Nestjs?

Resolvers provide the instructions for turning a GraphQL operation (a query, mutation, or subscription) into data. They return the same shape of data we specify in our schema -- either synchronously or as a promise that resolves to a result of that shape. Typically, you create a resolver map manually.

What is enum in Nestjs?

Enumeration types are a special kind of scalar that is restricted to a particular set of allowed values (read more here).

What is GraphQL in Nestjs?

GraphQL is a powerful query language for APIs and a runtime for fulfilling those queries with your existing data. It's an elegant approach that solves many problems typically found with REST APIs. For background, we suggest reading this comparison between GraphQL and REST.


1 Answers

According to the error,

Cannot determine a GraphQL input type for the "clubs". Make sure your class is decorated with an appropriate decorator

You're trying to use Club class as a GraphQL input type while it is already an object type (According to @ObjectType annotation you use).


Solution 1:

I would suggest you write a separate GraphQL input type like below (not tested). This is cleaner and less coupled if you need to separate the way you treat input and output of Club.

import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class ClubInput {
 @Field()
 president: string;

 @Field()
 members?: number;
}

Then in your resolver, you can use it like below.

@Query(() => Int)
async addClubs(@Args({name: 'clubs', type: () => [ClubInput]}) clubs: Array<ClubInput>) {
 // handle stuff
}

Solution 2:

But if you really need to use the same class for both purposes, you could try to create both input and object types with the same Club name. The name of the object type is by default the name of the class. So you need to provide the name explicitly to avoid conflict.

import { Field, Int, ObjectType, InputType } from '@nestjs/graphql';

@InputType("ClubInput")
@ObjectType("ClubType")
export class Club {
 @Field(type => String)
 president: string;

 @Field(type => Int)
 members?: number;
}

Now your Club has different names for input and object types. Then in your resolver, you can use it like below.

@Query(() => Int)
async addClubs(@Args({name: 'clubs', type: () => [ClubInput]}) clubs: Array<ClubInput>) {
 // handle stuff
}

Note: In this solution, you need to make sure that Club will never contain fields that express circular references or references to interfaces and unions. If that is going to happen, you will have to move to Solution 1, else this will make your input throw an error.


The take away is that in Typescript GraphQL, InputType and ObjectType are two different concepts and we need to use it properly to avoid any issues.

like image 131
Eranga Heshan Avatar answered Sep 18 '22 16:09

Eranga Heshan