I'm using type-graphql with apollo-server and I'm trying to handle errors using union types for example I want to return an arrary of GQLError(custom type) when something goes wrong in Query/Mutation. My code for Resolver, Entity and custom Union Types:
user/entity.ts
:
import {
BaseEntity,
Entity,
PrimaryColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
BeforeInsert
} from "typeorm";
import { ObjectType, Field, ID } from "type-graphql";
import { v4 as uuid } from "uuid";
import * as bcrypt from "bcrypt";
@Entity("users")
@ObjectType()
export class User extends BaseEntity {
@PrimaryColumn("uuid")
@Field(() => ID)
id: string;
@Column("varchar", { length: 255, unique: true })
@Field()
username: string;
@Column("varchar", { length: 255 })
@Field()
password: string;
@Column("varchar", { length: 255, nullable: true })
@Field()
email?: string;
@CreateDateColumn()
created: Date;
@UpdateDateColumn()
updated: Date;
@BeforeInsert()
async setup(): Promise<void> {
this.id = uuid();
this.password = await bcrypt.hash(this.password, bcrypt.genSaltSync(12));
}
}
user/types.ts
import { createUnionType } from "type-graphql";
import { GQLErrorResponse } from "../shared/index.entity";
import { User } from "./entity";
export const RegistrationResponse = createUnionType({
name: "UserRegistrationResponse",
types: () => [User, GQLErrorResponse] as const
});
export const LoginResponse = createUnionType({
name: "UserLoginResponse",
types: () => [User, GQLErrorResponse] as const
});
export const UserQueryResponse = createUnionType({
name: "UserQueryResponse",
types: () => [User, GQLErrorResponse] as const,
resolveType: (value: User | GQLErrorResponse) => {
if ("id" in value) {
return User;
} else {
return GQLErrorResponse;
}
}
});
export const UsersQueryResponse = createUnionType({
name: "UsersQueryResponse",
types: () => [User, GQLErrorResponse] as const
});
user/resolver.ts
import { Resolver, Arg, Query, Mutation } from "type-graphql";
import * as bcrypt from "bcrypt";
import * as _ from "lodash";
import { User } from "./entity";
import { UserRegistrationInput, UserLoginInput } from "./inputs";
import { UserQueryResponse, UsersQueryResponse } from "./types";
@Resolver(User)
export class UserResolver {
@Query(() => [User])
async users(): Promise<User[]> {
return User.find({});
}
@Query(() => UserQueryResponse)
async user(
@Arg("id", { nullable: true }) id?: string
): Promise<typeof UserQueryResponse> {
const user: User | undefined = await User.findOne(id);
if (_.isEmpty(user)) {
return {
errors: [
{
path: "id",
message: "User not found"
}
]
};
}
return user as User;
}
@Mutation(() => User)
async register(@Arg("input") input: UserRegistrationInput): Promise<User> {
const user: User = await User.create(input).save();
return user;
}
@Mutation(() => User)
async login(@Arg("input") input: UserLoginInput): Promise<User> {
const user: User | undefined = await User.findOne({
where: { username: input.username }
});
const valid: boolean = await bcrypt.compare(input.password, user.password);
if (!valid) {
throw new Error("Invalid username/password");
}
return user;
}
}
However, When I run my code I get the following error:
(node:325229) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'type' of undefined
I found the problem was due to circular dependencies, or rather: wrong order of imports.
Old answer:
It's just a guess, because for me that was the issue: have you tried to change the type of the
id
fields fromID
toInt
?Anyway, in my case, I found the origin of the problem when I changed the code of
type-graphql
in the line given in the error:So, I went in to that
schema-generator.js
and found this:It turned out, that
objectType
was alreadyundefined
, so I changed it to this:After that, I got the following error instead of
TypeError: Cannot read property 'type' of undefined
: