P
Prisma2mo ago
Manqo

Deleting User Account and All Related Data

I'm implementing a way for users to delete all their related data using Next.js 14 with App Router. This is my server action to handle data deletion after user confirmation:
'use server';
import { db } from '../lib/db';
import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({
region: AWS_REGION,
credentials: {
accessKeyId: AWS_KEY,
secretAccessKey: AWS_SECRET,
},
});

export async function deleteUserAccount(userId: string) {
try {

const resumes = await db.resume.findMany({
where: { userId },
include: {
personalInfo: true,
},
});
for (const resume of resumes) {
if (resume.personalInfo?.imagePath) {
const imageName = resume.personalInfo.imagePath.split('/').pop();
if (imageName) {
await s3.send(
new DeleteObjectCommand({
Bucket: process.env.AWS_BUCKET_NAME,
Key: imageName,
}),
);
}
}
}

await db.$transaction([
db.personalInfo.deleteMany({ where: { resume: { userId } } }),
db.workExperience.deleteMany({ where: { resume: { userId } } }),
...
db.resume.deleteMany({ where: { userId } }),
db.coverletter.deleteMany({ where: { userId } }),
db.account.deleteMany({ where: { userId } }),
db.session.deleteMany({ where: { userId } }),
db.user.delete({ where: { id: userId } }),
]);
return { success: true };
} catch (error) {
return { success: false, error: 'Failed to delete account' };
}
}
'use server';
import { db } from '../lib/db';
import { S3Client, DeleteObjectCommand } from '@aws-sdk/client-s3';
const s3 = new S3Client({
region: AWS_REGION,
credentials: {
accessKeyId: AWS_KEY,
secretAccessKey: AWS_SECRET,
},
});

export async function deleteUserAccount(userId: string) {
try {

const resumes = await db.resume.findMany({
where: { userId },
include: {
personalInfo: true,
},
});
for (const resume of resumes) {
if (resume.personalInfo?.imagePath) {
const imageName = resume.personalInfo.imagePath.split('/').pop();
if (imageName) {
await s3.send(
new DeleteObjectCommand({
Bucket: process.env.AWS_BUCKET_NAME,
Key: imageName,
}),
);
}
}
}

await db.$transaction([
db.personalInfo.deleteMany({ where: { resume: { userId } } }),
db.workExperience.deleteMany({ where: { resume: { userId } } }),
...
db.resume.deleteMany({ where: { userId } }),
db.coverletter.deleteMany({ where: { userId } }),
db.account.deleteMany({ where: { userId } }),
db.session.deleteMany({ where: { userId } }),
db.user.delete({ where: { id: userId } }),
]);
return { success: true };
} catch (error) {
return { success: false, error: 'Failed to delete account' };
}
}
This is the frontend function that gets called when confirm is clicked:
const handleDeleteAccount = async () => {
setIsDeleting(true);
const result = await deleteUserAccount(user.id);
setIsDeleting(false);
if (result.success) {
await signOut({ redirect: false });
router.push('/');
} else {
console.error(result.error);
}
};
const handleDeleteAccount = async () => {
setIsDeleting(true);
const result = await deleteUserAccount(user.id);
setIsDeleting(false);
if (result.success) {
await signOut({ redirect: false });
router.push('/');
} else {
console.error(result.error);
}
};
8 Replies
Manqo
ManqoOP2mo ago
What should I do to get rid of this error? The deletion actually works, and it redirects the user to the main page afterward, but having an error like this doesn't seem right. The problem is that I'm getting an error that says:
PrismaClientKnownRequestError: Invalid `prisma.session.delete()` invocation: An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.
at Cn.handleRequestError (.../library.js:123:6817)
at Cn.request (.../library.js:123:5926)
at async l (.../library.js:128:9968)
{
name: 'DeleteSessionError',
code: 'P2025'
}
PrismaClientKnownRequestError: Invalid `prisma.session.delete()` invocation: An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.
at Cn.handleRequestError (.../library.js:123:6817)
at Cn.request (.../library.js:123:5926)
at async l (.../library.js:128:9968)
{
name: 'DeleteSessionError',
code: 'P2025'
}
Here is part of my prisma schema.
model User {
id String @id @default(cuid())
name String
email String @unique
emailVerified DateTime?
image String?

accounts Account[]
sessions Session[]
resumes Resume[]

stripeCustomerId String? @unique @map(name: "stripe_customer_id")
stripeSubscriptionId String? @unique @map(name: "stripe_subscription_id")
stripePriceId String? @map(name: "stripe_price_id")
stripeCurrentPeriodEnd DateTime? @map(name: "stripe_current_period_end")

coverLetterGenerations Int @default(2)
Coverletter Coverletter[]
}

model Resume {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
title String
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
personalInfo PersonalInfo?
workexperiences WorkExperience[]
educations Education[]
...
fontFamily String @default("Helvetica")
templateStyle String @default("Example1")

workExperienceTitle String @default("Työkokemus")
...
}
model User {
id String @id @default(cuid())
name String
email String @unique
emailVerified DateTime?
image String?

accounts Account[]
sessions Session[]
resumes Resume[]

stripeCustomerId String? @unique @map(name: "stripe_customer_id")
stripeSubscriptionId String? @unique @map(name: "stripe_subscription_id")
stripePriceId String? @map(name: "stripe_price_id")
stripeCurrentPeriodEnd DateTime? @map(name: "stripe_current_period_end")

coverLetterGenerations Int @default(2)
Coverletter Coverletter[]
}

model Resume {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
title String
createdAt DateTime @default(now()) @map(name: "created_at")
updatedAt DateTime @updatedAt @map(name: "updated_at")
personalInfo PersonalInfo?
workexperiences WorkExperience[]
educations Education[]
...
fontFamily String @default("Helvetica")
templateStyle String @default("Example1")

workExperienceTitle String @default("Työkokemus")
...
}
idz
idz2mo ago
Maybe onDelete: Cascade could be a better way to achieve your goal? https://www.prisma.io/docs/orm/prisma-schema/data-model/relations/referential-actions
Referential actions | Prisma Documentation
Referential actions let you define the update and delete behavior of related models on the database level
Manqo
ManqoOP2mo ago
Thanks, so basically i sohuld be adding onDelete:cascade to all models that are related to user?
idz
idz2mo ago
yes and then delete the user and everything will be deleted aswell
Manqo
ManqoOP2mo ago
Okey so basically it should be enought that i call
await db.$transaction([
db.user.delete({ where: { id: userId } }),
]);
return { success: true };
await db.$transaction([
db.user.delete({ where: { id: userId } }),
]);
return { success: true };
idz
idz2mo ago
you dont even need the transaction this case
RaphaelEtim
RaphaelEtim2mo ago
In addition, the error message also says,
Invalid prisma.session.delete() invocation: An operation failed because it depends on one or more records that were required but not found. Record to delete does not exist.
Notice it says record to delete does not exist. Prisma delete method expects the record to exist and throws an error if it does not.
Manqo
ManqoOP2mo ago
Yeah i think i got The code working. The problem was with The nextauth, it tried to signout The user after The deletion.
Want results from more Discord servers?
Add your server