Prisma Explicit Many to Many - how to delete item without deleting user - postgresql

My use case is simple:
There's users
Users can create workspaces
Users can delete workspaces
User gets a role on each workspace (OWNER, ADMIN, MANAGER, VIEWER)
The problem:
This requires an explicit many-to-many relation (users <-> workspaces).
Creating workspaces is fine, it all works. Deleting a workspace is where the problem occurs. It either:
Deletes nothing
Or deletes everything (user + workspace + their relation table row) using onDelete: Cascade
I obviously don't want my users to get deleted.
Here's my Prisma schema:
model User {
id String #id #default(cuid())
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
email String? #unique
plan Plan? #relation(fields: [planId], references: [id], onDelete: Cascade)
planId String?
workspaces WorkspacesOnUsers[]
}
model Workspace {
id String #id #default(cuid())
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
name String?
users WorkspacesOnUsers[]
}
model WorkspacesOnUsers {
id String #id #default(cuid())
user User? #relation(fields: [userId], references: [id], onDelete: Restrict, onUpdate: Restrict)
userId String?
workspace Workspace? #relation(fields: [workspaceId], references: [id])
workspaceId String?
role Role? #default(VIEWER)
}
enum Role {
OWNER
ADMIN
MANAGER
VIEWER
}
And this is how I try to delete a workspace from a user:
await prisma.workspace.delete({
where: { id: workspaceId },
})
I also tried this but it only removes the row in the relation, but I still can't remove the workspace after that (Foreign key constraint failed on the field: WorkspacesOnUsers_userId_fkey (index)):
await prisma.workspace.update({
where: { id: workspaceId },
data: {
users: { deleteMany: {} },
},
})
Someone help please!

I had a similar issue but with users deleting posts, this is how I deleted a post attached to a user via there id (in this case their sessionId)
await prisma.user.update({
where: { sessionId: sessionId },
data: {
Post: {
delete: {
id: postId,
},
},
},
});
in your case it will look something like this
await prisma.user.update({
where: { id: userIdFromFunction },
data: {
Workspace: {
delete: {
id: workspaceIdFromFunction,
},
},
},
});
Hope this helps!

Related

Unable to pass multiple filters in Prisma where clause

I am using Prisma, MongoDB and NextAuth in a Next js project (Typescript). I'm trying to pass multiple filters in the where clause, but it returns a null array.
In the app there are 2 pages, one is called My Feed where anyone (even if not logged in) can see everyone's published posts. Second page is called My Drafts, where logged in users can view their unpublished posts, and choose to publish or delete them.
Basically, when a user creates a new post, it has a property - published, which is false by default. I am trying to show these unpublished posts, in the my drafts page of a logged in user.
export const getServerSideProps: GetServerSideProps = async ({ req, res }) => {
const session = await getSession({ req });
if (!session) {
res.statusCode = 403;
return { props: { drafts: [] } };
}
const drafts = await prisma.post.findMany({
where: {
published: false,
author : {email: session.user?.email}
},
include: {
author: {
select: { name: true },
},
},
});
return {
props: { drafts },
};
};
If I remove the author from the where clause, it returns all unpublished posts from all users. If I remove the published:false or keep both - published and email then I'm getting null array, although session is defined.
Here is my prisma schema : -
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id String #id #default(auto()) #map("_id") #db.ObjectId
title String
content String?
published Boolean #default(false)
author User? #relation(fields: [authorId], references: [id])
authorId String?
}
model Account {
id String #id #default(auto()) #map("_id") #db.ObjectId
userId String #map("user_id")
type String
provider String
providerAccountId String #map("provider_account_id")
refresh_token String? #db.String
access_token String? #db.String
expires_at Int?
token_type String?
scope String?
id_token String? #db.String
session_state String?
oauth_token_secret String?
oauth_token String?
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
##unique([provider, providerAccountId])
}
model Session {
id String #id #default(auto()) #map("_id") #db.ObjectId
sessionToken String #unique #map("session_token")
userId String #map("user_id")
expires DateTime
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String #id #default(auto()) #map("_id") #db.ObjectId
name String?
email String? #unique
emailVerified DateTime?
image String?
posts Post[]
accounts Account[]
sessions Session[]
}
model VerificationToken {
id String #id #default(auto()) #map("_id") #db.ObjectId
identifier String
token String #unique
expires DateTime
##unique([identifier, token])
}

Prisma update query only allowing an update by the ID field and not anything else

const response = await prisma.teamMember.update({
where: {
teamId,
userId: memberToEditId,
},
data: {
role,
},
});
Argument where of type TeamMemberWhereUniqueInput needs exactly one argument, but you provided teamId and userId. Please choose one. Available args:
type TeamMemberWhereUniqueInput {
id?: String
}
Unknown arg `teamId` in where.teamId for type TeamMemberWhereUniqueInput. Did you mean `id`? Available args:
type TeamMemberWhereUniqueInput {
id?: String
}
Unknown arg `userId` in where.userId for type TeamMemberWhereUniqueInput. Did you mean `id`? Available args:
type TeamMemberWhereUniqueInput {
id?: String
}
Hey guys. I'm trying to update a specific document based off of a specific value(s) in my table, but it only seems to let me use the primary key for the table? My schema looks like:
model TeamMember {
id String #id #default(cuid())
teamId String
userId String
role Role #default(MEMBER)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
team Team #relation(fields: [teamId], references: [id], onDelete: Cascade)
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
}
model Team {
id String #id #default(cuid())
name String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
TeamMember TeamMember[]
}
model User {
id String #id #default(cuid())
name String?
email String? #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
TeamMember TeamMember[]
theme Theme #default(LIGHT)
}
To fix this, temporarliy I can make a separate findFirst query, and use the returned row to get the ID of the row which i want to update. This is fine, however I know it can be done without doing this, and it is a little ugly having two queries when one can work just fine.
Any help would be greatly appreciated.
Prisma needs to uniquely identify a single record which needs to be updated, due to which the whereUnique constraint is enforced.
For now, you can use updateMany as a workaround for this, this unique constraint is not enforced for updateMany

How to select a custom User model property?

I added a company property to the User model in my prisma.schema file (The rest of the prisma.schema file is still similar to the one in the documentation: https://next-auth.js.org/adapters/prisma)
model User {
id String #id #default(cuid())
name String?
email String? #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
company Company?
}
model Company {
id Int #id #default(autoincrement())
companyName String #unique
gender String
firstName String
lastName String
street String
houseNumber Int
postcode Int
city String
country String
countryCode String
callNumber Int
emailAddress String
website String?
socials Json?
companyUser User #relation(fields: [companyUserId], references: [id])
companyUserId String #unique
}
The whole authentification process is working fine even after the change but when I try to select a User from the database it only returns a certain portion of the User namely the id, name, email, emailVerified and image property.
How can I change this behaviour?
const user = await prisma.user.findUnique({
where: {
id: ...
}
})
For sure I could only create the Company model without connecting it to the User model and maybe adding the User's id to it to have an implicit connection, but that's undermining the whole purpose...
you're looking for nested reads, if you want to include the whole company model you should use include with the name of the relation, note that this will return all the fields for that specific relation:
const user = await prisma.user.findUnique({
where: {
id: ...
},
include: {
company: true,
},
})
if you want to return specific relation fields with the whole user you should use select inside include:
const user = await prisma.user.findUnique({
where: {
id: ...
},
include: {
company: {
select : {
firstName: true,
},
},
},
})

Prisma: Model with three unique index fields referencing optional fields

I got a question regarding unique indexes with optional values.
I have a schema like this:
model Thread {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
body String
user User #relation(fields: [userId], references: [id])
userId Int
likes Like[] #relation("ThreadsOnLikes")
}
model Like {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
user User #relation(fields: [userId], references: [id])
userId Int
thread Thread? #relation("ThreadsOnLikes", fields: [threadId], references: [id])
threadId Int?
comment Comment? #relation("CommentsOnLikes", fields: [commentId], references: [id])
commentId Int?
##unique([userId, threadId, commentId])
}
And inside a resolver I want to for example delete a like from a user for a specific threadId like so:
await db.thread.update({
where: { id: input.id },
data: {
likes: {
delete: {
userId_threadId_commentId: { userId: session.userId, threadId: input.id },
},
}
}
})
But when I try to execute that mutation, prisma throws the following error:
Argument commentId for data.likes.delete.userId_threadId_commentId.commentId is missing.
When I add it to the delete argument with , commentId: null it states this error:
Argument commentId: Got invalid value null on prisma.updateOneThread. Provided null, expected Int.
Although inside the database the comment_id field is actually null . Is this a bug or how is this fixable?
From the docs:
All fields that make up the unique constraint must be mandatory fields. The following model is not valid because id could be null:
model User {
firstname Int
lastname Int
id Int?
##unique([firstname, lastname, id])
}
The reason for this behavior is that all connectors consider null values to be distinct, which means that two rows that look identical are considered unique:
firstname | lastname | id
-----------+----------+------
John | Smith | null
John | Smith | null
I am not sure why Prisma is unable to validate schema beforehand in "compile time", maybe it is a bug, so I suggest you to maybe create an issue on Github?

How to get sql result with only rows that has a connected relations row

I am trying to query a database using the Prism Client and want to get back only the rows that has a foreign key in a joined table.
For example, get all users who have also created a post. I need a kind of "INNER JOIN".
I have tried something like:
return this.prisma.user.findMany({
where: {
Post: {
some: {
id: {
not: 0,
}
}
}
}
});
But the result is not the correct one.
I'm not sure how to use "none, some or every"
Suppose I have a schema like this:
model User {
id Int #default(autoincrement()) #id
name String
posts Post[]
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Post {
id Int #default(autoincrement()) #id
title String
user User? #relation(fields: [userId], references: [id])
userId Int?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
I get the users without any posts via the following query:
const usersWithoutPosts = await prisma.user.findMany({
where: {
posts: {
none: {
id: undefined,
},
},
},
})
Could you try this way and check?