2
votes

I'm trying to figure out cascade deletion in GraphQL.

I'm attempting to delete a node of type Question, but type QuestionVote has a required relation to Question. I'm looking for a way to delete a Question and all its votes at once.

Mutation for deleting a Question:

type Mutation {
  deleteQuestion(where: QuestionWhereUniqueInput!): Question!
}

And its resolver (I'm using Prisma):

function deleteQuestion(parent, args, context, info) {
  const userId = getUserId(context)  
  return context.db.mutation.deleteQuestion(
      {
        where: {id: args.id}
      },
      info,
  )
}

How can I modify that mutation to also delete related QuestionVote nodes? Or should I add a separate mutation that deletes one or multiple instances of QuestionVote?

In case it's important, here are the mutations that create Question and QuestionVote:

function createQuestion(parent, args, context, info) {
    const userId = getUserId(context)
    return context.db.mutation.createQuestion(
        {
            data: {
              content: args.content,
              postedBy: { connect: { id: userId } },
            },
        },
        info,
    )
}

async function voteOnQuestion(parent, args, context, info) {
  const userId = getUserId(context)

  const questionExists = await context.db.exists.QuestionVote({
    user: { id: userId },
    question: { id: args.questionId },
  })
  if (questionExists) {
    throw new Error(`Already voted for question: ${args.questionId}`)
  }

  return context.db.mutation.createQuestionVote(
    {
      data: {
        user: { connect: { id: userId } },
        question: { connect: { id: args.questionId } },
      },
    },
    info,
  )
}

Thanks!

1

1 Answers

10
votes

You can set up cascade deletion by modifying your datamodel.

Given your question, I assume your datamodel looks somewhat like this:

type Question {
  id: ID! @unique
  votes: [QuestionVote!]! @relation(name: "QuestionVotes")
  text: String!
}

type QuestionVote {
  id: ID! @unique
  question: Question @relation(name: "QuestionVotes")
  isUpvote: Boolean!
}

Then you have to add the onCascade: DELETE field to the @relation directive like so:

type Question {
  id: ID! @unique
  votes: [QuestionVote!]! @relation(name: "QuestionVotes" onDelete: CASCADE)
  text: String!
}

type QuestionVote {
  id: ID! @unique
  question: Question @relation(name: "QuestionVotes")
  isUpvote: Boolean!
}

Now, every time a Question node is deleted, all related QuestionVote nodes are also deleted.

Note: If omitting onDelete, the value is automatically set to onDelete: SET_NULL by default. This means that deleting a node results in setting the other side of the relation to null.

You can read more about cascading deletes in Prisma in the documentation.