Search code examples
graphqlnestjstypegraphql

setup multiple return types for the NestJs GraphQL Query decorator


I want to create a GraphQL API using NestJs. As far as I understood I won't be throwing HTTP exceptions for invalid requests anymore. Therefore I think I have to create my own "error codes" I can send back to the client. So given this basic example

@ObjectType()
export class ErrorResponse {
  @Field()
  message: string;
}

I have a service function to return a user by its ID and I extended the return type to return an error object if the request was invalid.

  public async getUserById(id: number): Promise<ErrorResponse | User> {
    const user: User = await this.usersRepository.findOne(id);

    if (!user) {
      const errorResponse: ErrorResponse = new ErrorResponse();
      errorResponse.message = `User with ID ${id} does not exist`;
      return errorResponse;
    }

    return user;
  }

The resolver originally was something like

  @Query(() => User)
  public async user(@Args('id') id: number): Promise<ErrorResponse | User> {
    return this.usersService.getUserById(id);
  }

but as mentioned above it's also possible to return a ErrorResponse if the id does not exist. How can I design the Query decorator to provide multiple return types?

@Query(() => ErrorResponse | User)

won't do the trick and shows up with this error

The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.ts(2362)


Solution

  • This is the solution that i came up for a similar situation. GraphQL expects single return ObjectType. First i created a common Object

    @ObjectType()
    export class MutationResult {
      @Field({ nullable: true })
      success?: boolean;
    
      @Field({ nullable: true })
      error?: boolean;
    }
    

    Then in the user module i created 2 objects types - User and UserResponse. On UserResponse i extened the common MutationResult Object

    @ObjectType()
    export class User {
      @Field(type => ID)
      id: string;
    
      @Field()
      name: string;
    }
    
    @ObjectType()
    export class UserResponse extends MutationResult {
      @Field()
      result: User;
    }
    

    Now in query you can do this

    mutation {
      addUser(name: "Test") {
        success,
        error,
        result {
          name
        }
      }
    }