I am trying to figure out how to delete a record created in my graphql, apollo, prisma app, where the record I am deleting may have a has many relationship to another model.
I have a model called IssueGroups. IssueGroups have many issues.
I have a model with:
import * as Prisma from "@prisma/client"
import { Field, ObjectType } from "type-graphql"
import { BaseModel } from "../shared/base.model"
@ObjectType()
export class IssueGroup extends BaseModel implements Prisma.IssueGroup {
@Field()
title: string
@Field()
description: string
@Field(type => String)
issues: Prisma.Issue[];
}
A resolver with:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
import { IssueGroupService } from "./issueGroup.service"
import { IssueGroupInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
@Service()
@Resolver(() => IssueGroup)
export default class IssueGroupResolver {
@Inject(() => IssueGroupService)
issueGroupService: IssueGroupService
@Query(() => [IssueGroup])
async allIssueGroups() {
return await this.issueGroupService.getAllIssueGroups()
}
@Query(() => IssueGroup)
async issueGroup(@Arg("id") id: string) {
return await this.issueGroupService.getIssueGroup(id)
}
@Mutation(() => IssueGroup)
async createIssueGroup(@Arg("data") data: IssueGroupInput) {
return await this.issueGroupService.createIssueGroup(data)
}
// : Promise<IssueGroup[]> {
@Mutation(() => IssueGroup)
async updateIssueGroup(
@Arg("id") id: string,
@Arg("data") data: IssueGroupInput
) {
return await this.issueGroupService.updateIssueGroup(id, data)
}
@Mutation(() => IssueGroup)
async deleteIssueGroup(@Arg("id") id: string) {
return await this.issueGroupService.deleteIssueGroup(id)
}
}
// private readonly issueGroupService: IssueGroupService
// @Query(() => [IssueGroup])
// async issueGroups(): Promise<IssueGroup[]> {
// return this.issueGroupService.findAll()
// }
// @Query(() => IssueGroup)
// async issueGroup(@Arg("id") id: string): Promise<IssueGroup> {
// return this.issueGroupService.findOne(id)
// }
// @Mutation(() => IssueGroup)
// async createIssueGroup(
// @Arg("data") data: IssueGroupInput
// ): Promise<IssueGroup> {
// return this.issueGroupService.create(data)
// }
A service with:
import { prisma } from "../../lib/prisma"
import { Service } from "typedi"
import { IssueGroupInput } from "./inputs/create.input"
import { Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
@Service()
@Resolver(() => IssueGroup)
export class IssueGroupService {
async createIssueGroup(data: IssueGroupInput) {
return await prisma.issueGroup.create({
data,
})
}
async deleteIssueGroup(id: string) {
return await prisma.issueGroup.delete({ where: { id } })
}
async updateIssueGroup(id: string, data: IssueGroupInput) {
const issueGroup = await prisma.issueGroup.findUnique({ where: { id } })
if (!issueGroup) {
throw new Error("Issue not found")
}
return await prisma.issueGroup.update({ where: { id }, data })
}
async getAllIssueGroups() {
return (await prisma.issueGroup.findMany({orderBy: {title: 'asc'}}))
}
async getIssueGroup(id: string) {
return await prisma.issueGroup.findUnique({
where: {
id,
},
})
}
}
When I try to delete the issueGroup (which does not currently have any issues, I can see an error that points to issue.delete and says:
operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.
In my form I have:
import * as React from "react"
import { gql } from "@apollo/client"
import type { IssueGroupInput } from "lib/graphql"
import { QueryMode, Role, SortOrder, useAllIssueGroupsQuery, useDeleteIssueGroupMutation } from "lib/graphql"
const __ = gql`
query AllIssueGroups {
allIssueGroups {
id
title
description
}
}
mutation deleteIssueGroup($id: String!) {
deleteIssueGroup(id: $id) {
id
title
description
issues // I have tried both with and without issues in this list
}
}
`
export default function IssueGroups() {
const [deleteIssueGroup] = useDeleteIssueGroupMutation()
const { data, loading, refetch } = useAllIssueGroupsQuery({
fetchPolicy: "cache-and-network",
})
const allIssueGroups = data?.allIssueGroups
const onDeleteIssueGroup = (id: string) => {
return (
deleteIssueGroup({ variables: { id } }).then(() => refetch())
)
}
return (
<List>
{data?.allIssueGroups.map((issueGroup) => (
<ListItem key={issueGroup.id}>
{issueGroup.title}{issueGroup.description}
<Button onClick={() => onDeleteIssueGroup(issueGroup.id)}>Delete</Button>
</ListItem>
))}
</List>
)
}
I have seen this page of the prisma documentation and I think I have followed its advice for how to deal with an issue when the parent issueGroup is deleted.
model IssueGroup {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
title String
description String
issues Issue[]
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
}
model Issue {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
title String
description String
issueGroup IssueGroup @relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String @db.Uuid
subscribers UserIssue[]
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
}
However, VS code thinks this is an error. It gives me an error message that says:
The
onDelete
referential action of a relation should not be set toSetNull
when a referenced field is required. We recommend either to choose another referential action, or to make the referenced fields optional.
My db is psql, which the prisma docs suggest, should allow SetNull
PostgreSQL PostgreSQL is the only database supported by Prisma that allows you to define a SetNull referential action that refers to a non-nullable field. However, this raises a foreign key constraint error when the action is triggered at runtime.
For this reason, when you set postgres as the database provider in the (default) foreignKeys relation mode, Prisma warns users to mark as optional any fields that are included in a @relation attribute with a SetNull referential action. For all other database providers, Prisma rejects the schema with a validation error.
I can see from this page of the docs that lists cannot be optional, so I dont think I can follow the advice in the relational page of the docs and make the issueGroup issues[] as optional.
How can i delete the issue (if there were any) at the same time as I delete the issue group?
Just make issueGroup
optional
model IssueGroup {
...
issues Issue[]
...
}
model Issue {
...
issueGroup IssueGroup? @relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String? @db.Uuid
...
}
The actual message from prisma is saying that
Prisma warns users to mark as optional any fields that are included in a @relation attribute with a SetNull referential action
Your relationship has onDelete: SetNull
- so mark it as optional