#typescript #express #graphql #apollo-server #typegraphql
#typescript #выразить #graphql #apollo-сервер #typegraphql
Вопрос:
Я использую type-graphql с apollo-server и пытаюсь обрабатывать ошибки, используя типы объединения, например, я хочу вернуть ошибку GQLError (пользовательский тип), когда что-то идет не так в запросе / мутации. Мой код для типов распознавателя, сущности и пользовательского объединения:
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;
}
}
Однако, когда я запускаю свой код, я получаю следующую ошибку:
(node:325229) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'type' of undefined
Ответ №1:
Я обнаружил, что проблема связана с циклическими зависимостями, а точнее: неправильным порядком импорта.
Старый ответ:
Это всего лишь предположение, потому что для меня это была проблема: вы пытались изменить тип id
полей с ID
на Int
?
В любом случае, в моем случае я обнаружил причину проблемы, когда изменил код type-graphql
в строке, указанной в ошибке:
узел: 28896) UnhandledPromiseRejectionWarning: ошибка типа: не удается прочитать свойство «тип» неопределенного в interfaceClasses.map.InterfaceClass (/workspaces/assistant-private/node_modules/type-graphql/dist/schema/schema-generator.js:164:149)
Итак, я пошел к этому schema-generator.js
и нашел это:
types: () => unionClassTypes.map(objectType => this.objectTypesInfo.find(type => type.target === objectType).type),
Оказалось, это objectType
уже undefined
было, поэтому я изменил его на это:
types: () => unionClassTypes.filter(a => a).map(objectType => this.objectTypesInfo.find(type => type.target === objectType).type),
После этого я получил следующую ошибку вместо TypeError: Cannot read property 'type' of undefined
:
Ошибка GraphQL: поле интерфейса IBaseEntity.id ожидает тип Int! но BaseAction.id это тип Float!.