How to select a custom User model property? - postgresql

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,
},
},
},
})

Related

name: 'LinkAccountError', [next-auth][error][adapter_error_linkAccount]

I am making a sign up page with 3 providers (Twitter, Facebook and Instagram) using next-auth and prisma with mongoDB. The issue appears when I try to sign up with any of the providers. I think the prisma schema is the problem. Here is the error that I receive:
Invalid `p.account.create()` invocation in
C:\...\node_modules\#next-auth\prisma-adapter\dist\index.js:19:42
16 },
17 updateUser: ({ id, ...data }) => p.user.update({ where: { id }, data }),
18 deleteUser: (id) => p.user.delete({ where: { id } }),
→ 19 linkAccount: (data) => p.account.create({
data: {
provider: 'instagram',
type: 'oauth',
providerAccountId: '62921xxxxxx98535',
access_token: 'IGQVJYdU...',
user_id: 178xxxx,
~~~~~~~
userId: '63e2538xxxx'
}
})
Unknown arg `user_id` in data.user_id for type AccountUncheckedCreateInput. Did you mean `userId`? Available args:
type AccountUncheckedCreateInput {
id?: String
userId: String
type: String
provider: String
providerAccountId: String
}
I have red all the documentations about, chatGPT also didn't help much. The prisma schema looks like this:
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String #id #default(auto()) #map("_id") #db.ObjectId
name String?
email String? #unique
emailVerified DateTime? #map("email_verified")
image String?
accounts Account[]
sessions Session[]
##map("users")
}
model Account {
id String #id #default(auto()) #map("_id") #db.ObjectId
userId String #db.ObjectId
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?
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
##unique([provider, providerAccountId])
##map("accounts")
}
model Session {
id String #id #default(auto()) #map("_id") #db.ObjectId
sessionToken String #unique #map("session_token")
userId String #db.ObjectId
expires DateTime
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
##map("sessions")
}
model VerificationToken {
identifier String #id #default(auto()) #map("_id") #db.ObjectId
token String #unique
expires DateTime
##unique([identifier, token])
##map("verificationtokens")
}
You are passing user_id parameter while creating an account, but there is no field named user_id in the Account model in your schema file.
If you need to pass user_id while creating an account then you need to define it in the Account model as well.

How can I have a "Universal" id system in prisma that returns a typed object?

Let's say I have two database tables one for images and one for videos, they both have cuid primary keys, and I want to do a find with a key for instance "cld7q83rc000008mfc3pq5q6a" that could be an image or it could be a video how do I set my database up in this way?
It seems I have a couple of options:
A "universal key" table that is non-relational? id, table_name, item_id
A "universal key" table that is relational? id, image_id, video_id, ...etc
A VIEW that somehow generates this table dynamically?
A joined query with all tables
I'm interested in maintain type information
function findAny (cuid: string) {
const universal = await prisma.universal.findUnique({
where: {
id: 'cld7q83rc000008mfc3pq5q6a',
},
})
if (universal.image) {
return {...universal.image, extraImageInfo: true }
} else {
return getTypeFromUniversalFind(universal)
}
}
Update:
Option 1:
model Image {
uuid String #db.Uuid #default(uuid()) #unique
src String
}
model Video {
uuid String #db.Uuid #default(uuid()) #unique
src String
}
model UniversalKey {
uuid String #db.Uuid #default(uuid()) #unique
table String # "image" or "video"
itemUuid String # not an actual relation
}
Option 2:
model Image {
uuid String #db.Uuid #default(uuid()) #unique
src String
universalKey UniversalKey?
}
model Video {
uuid String #db.Uuid #default(uuid()) #unique
src String
universalKey UniversalKey?
}
model UniversalKey {
uuid String #db.Uuid #default(uuid()) #unique
image Image? #relation(fields: [imageId], references: [uuid])
imageId String? #unique
video Video? #relation(fields: [videoId], references: [uuid])
videoId String? #unique
# ... you have to update this for every new type
}

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 Explicit Many to Many - how to delete item without deleting user

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!

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?