Typegraphql + TypeORM: сохранить / обновить массив сущностей
У меня есть сервер graphql и roleтаблица, и я хотел бы сохранить несколько ролей в рамках одной мутации.
Я много искал, но ничего не нашел.
Как я могу сделать что-то вроде:
mutation {
addRoles (roles: [
{
name: "role1",
description: "role 1"
},
{
name: "role2",
description: "role 2"
},
...
])
}
Другими словами, как реализовать addRolesи updateRolesрезолвер?
Цикл for - единственный вариант? Можно ли сохранить все роли за один вызов БД?
roleМодель:
@Entity("role")
@ObjectType()
export class Role extends BaseEntity {
@Field((type) => Number)
@PrimaryGeneratedColumn()
readonly id!: number;
@Field()
@Column({ length: 64 })
name!: string;
@Field({ nullable: true })
@Column({ length: 512, nullable: true })
description!: string;
}
И добавляем и обновляем резолвер:
@Resolver((of) => Role)
export class RoleResolver {
@Mutation((returns) => Boolean)
async addRole(
@Arg("role") role: AddRoleInput
): Promise<Boolean> {
const roleExists = await Role.count({ name: role.name });
if (roleExists > 0)
throw new Error(`Role with name "${role.name}" already exists!`);
const newRole = Role.create(role);
await newRole.save();
return true;
}
@Mutation((returns) => Boolean)
async updateRole(
@Arg("role") role: UpdateRoleInput
): Promise<Boolean> {
const oldRole = await Role.findOneOrFail(role.id);
Object.assign(oldRole, role);
await oldRole.save();
return true;
}
}
И AddRoleInputиUpdateRoleInput
@InputType({ description: "New Role Argument" })
export class AddRoleInput implements Partial<Role> {
@Field()
name!: string;
@Field({ nullable: true })
description?: string;
}
@InputType({ description: "Update Role Argument" })
export class UpdateRoleInput implements Partial<Role> {
@Field()
id!: number;
@Field()
name!: string;
@Field({ nullable: true })
description?: string;
}
Ответы
Суть того, что вам нужно сделать, это определить addRolesполе типа мутации в вашей схеме. Например
export const RoleSchema = gql`
type Role {
id: Int
# Other fields omitted for brevity
}
extend type Mutation {
# This should already exist
addRole(role: AddRoleInput!): Role!
# To accept multiple Input Types, use the List construct
addRoles(roles: [AddRoleInput!]!): [Role!]
}
`;
Затем определите addRolesв своем преобразователе и вызовите соответствующий API.
Мне удалось найти ответ методом проб и ошибок. Я отправлю его здесь, если у кого-то будет такая же проблема:
@Resolver((of) => Role)
export class RoleResolver {
@Mutation((returns) => Boolean)
async addRoles(
@Arg("roles", type => [AddRoleInput]) roles: AddRoleInput[]
): Promise<Boolean> {
// also checks at front
const roleExists = await Role.count({
name: In(roles.map((role) => role.name)),
});
if (roleExists > 0)
throw new Error(`Role name conflict!`);
const newRoles = []
for (let i=0, i < roles.length, i++) {
const newRole = Role.create(roles[i]);
newRoles.push(newRole);
}
// this is the solution
// turns out an entity class can be used to save instances
// failed to found this in the docs though
await Role.save(newRoles);
return true;
}
...
}
Я оставлю награду открытой на случай, если будут ответы получше.