Update parent and upsert child in Prisma - node.js

I have a parent-child relation like below:
model User {
id Int #id #default(autoincrement())
authId String #unique #default("N/A") #map("auth_id") #db.VarChar(128)
email String #unique #db.VarChar(256)
firstName String #map("first_name") #db.VarChar(64)
lastName String #map("last_name") #db.VarChar(64)
profile Profile?
}
model Gender {
id Int #id #default(autoincrement())
text String
Profile Profile[]
}
model MedicalCondition {
id Int #id #default(autoincrement())
text String
Profiles Profile[]
}
model Profile {
id Int #id #default(autoincrement())
dob String #db.VarChar(32)
weightLbs Float #map("weight_lbs")
heightIn Int #map("height_in") #db.SmallInt
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
userId Int #unique #map("user_id")
gender Gender #relation(fields: [genderId], references: [id])
genderId Int #map("gender_id")
lifeStyle LifeStyle? #relation(fields: [lifeStyleId], #map("life_style_id")
medicalConditions MedicalCondition[]
}
I'm quite new to node.js and Prisma and I'm wondering is it possible to update User and upsert Profile together like below?
const updateWithProfile = (where: IUserUniqueInput, data: IUserUpdate, medicalConditions?: IMedicalConditionWhereUniqueInput[]): Promise<IUser> => {
return user.update({
where,
data:{
...data,
profile: {
upsert:{
????
},
}
}
});
};

Here is my working solution for upsert one-to-one and many-to-many relations:
const update = async (
data: IProfileUpdate,
medicalConditions?: IMedicalConditionWhereUniqueInput[]
): Promise<IProfile> => {
return profile.upsert({
where: {
userId: data.userId,
},
update: {
...data,
// set removes previous values in mapping table and connect inserts new IDs
medicalConditions: { set: [], connect: medicalConditions }
},
create: {
...data,
medicalConditions: { connect: medicalConditions }
}
});
};

Related

Can't update the related table on prisma

First of all this is my schema.prisma file. The problem is when i try to update a payment to complete and then update the wallet value in the same query.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
uid String #id #default(cuid())
created_at DateTime
username String
roles String[]
accessToken String
session Session[]
walletId String #unique
wallet Wallet #relation(fields: [walletId], references: [id])
payment Payment[]
}
model Session {
id String #id #default(uuid())
userid String
expires DateTime #db.Timestamptz
cookieId String #unique
user User #relation(fields: [userid], references: [uid])
}
model Wallet {
id String #id #default(uuid())
balance Int #default(0)
user User?
payment Payment[]
}
model Order {
id String #id #default(uuid())
createdAt DateTime #default(now())
product Product #relation(fields: [productId], references: [id]) //Note that only one product can be ordered at a time
payment Payment #relation(fields: [paymentId], references: [piPaymentId])
productId String
paymentId String #unique
}
model Payment {
piPaymentId String #id #unique
amount Float
txid String #default("")
status PaymentStatus #default(PENDING)
user User #relation(fields: [userId], references: [uid])
order Order?
wallet Wallet #relation(fields: [walletId], references: [id])
walletId String
userId String
}
model Product {
id String #id #default(uuid())
name String
price Float
amount Int //Note at this moment we only support coins as a product
order Order[]
}
enum PaymentStatus {
PENDING
PAID
FAILED
CANCELLED
}
First I create a new payment with any problems:
async create(payment: APIRequests.Paymnet.Create) {
return await this.db.prisma.payment.create({
data: {
piPaymentId: payment.paymentId,
user: {
connect: {
uid: payment.userId,
},
},
amount: payment.amount,
status: "PENDING",
wallet: {
connect: {
id: payment.walletId
}
}
}
});
}
And then after some events in the backend i need to update the state of the payment as PAID and update the user's wallet as well.
async complete(payment: APIRequests.Paymnet.Complete) {
await this.db.prisma.payment.update({
where: {
piPaymentId: payment.paymentId
},
data: {
status: "PAID",
txid: payment.txid,
wallet: {
update: {
balance: {
decrement: payment.amount
}
}
}
}
});
}
the problem is here when I try to execute this query i get this error message...
Error:
Invalid `prisma.payment.update()` invocation:
{
where: {
piPaymentId: 'some paymentID'
},
data: {
status: 'PAID',
txid: 'some txid',
wallet: {
~~~~~~
update: {
balance: {
decrement: 0.1
}
}
}
}
}
Unknown arg `wallet` in data.wallet for type PaymentUncheckedUpdateInput. Did you mean `walletId`? Available args:
type PaymentUncheckedUpdateInput {
piPaymentId?: String | StringFieldUpdateOperationsInput
amount?: Float | FloatFieldUpdateOperationsInput
txid?: String | StringFieldUpdateOperationsInput
status?: PaymentStatus | EnumPaymentStatusFieldUpdateOperationsInput
order?: OrderUncheckedUpdateOneWithoutPaymentNestedInput
walletId?: String | StringFieldUpdateOperationsInput
userId?: String | StringFieldUpdateOperationsInput
}
Thanks for your help!

Can't disconnect relation of explicit many-to-many

I'm trying to delete some users which are related to a group.
Here is the schema:
model User {
id String #id #default(cuid())
username String
email String #unique
password String?
group GroupUser[]
}
model Group {
id String #id #default(cuid())
name String
user GroupUser[]
}
model GroupUser{
userId String
user User #relation(fields: [userId],references: [id],onDelete: Cascade,onUpdate:Cascade)
groupId String
group Group #relation(fields: [groupId],references: [id],onDelete: Cascade,onUpdate: Cascade)
##id([userId,groupId])
}
The code to delete the users:
async deleteUsersFromGroup(id: string, userData: UpdateGroupDto): Promise<number> {
const deletedUsers = await prisma.group.update({
where: {
id: id,
},
data: {
user: { disconnect: /* trying to put the array of users id here */ },
},
});
return deletedUsers.length;
}
The problem is that I want to give the userID inside of the disconnect but it is asking me for userId_groupId which is the relational key.
You would need to delete the record from the connecting table i.e. GroupUser.
You can try something like this:
await prisma.groupuser.delete({
where: {
userId_groupId: {
userId: 'userId',
groupId:'groupId'
}
}
});
If the connecting records are deleted then both the entities would be disconnected.
Since I wanted to delete multiple users at the same time I used the map function inside userId, resorting to prisma.groupUser.delete().
async deleteUsersFromGroup(id: string, userData: DeleteGroupDto): Promise<any> {
const response = await prisma.groupUser.deleteMany({
where: {
groupId: id,
userId: { in: userData.users.map(user => user.userId) },
},
});
return response.count
}

Prisma and MongoDB: Many-to-many nested create only populates one side of the relation

I have a schema set up where Users can create many Photos, and then attach those photos to a single Post. Other users can repost others' photos, so I need a many to many relation between Photo and Post:
model User {
id String #id #default(auto()) #map("_id") #db.ObjectId
photos Photo[]
posts Post[]
...
}
model Photo {
id String #id #default(auto()) #map("_id") #db.ObjectId
user User #relation(fields: [userId], references: [id])
userId String #db.ObjectId
posts Post[] #relation(fields: [postIds], references: [id])
postIds String[] #db.ObjectId
...
}
model Post {
id String #id #default(auto()) #map("_id") #db.ObjectId
poster User #relation(fields: [posterId], references: [id])
posterId String #db.ObjectId
photos Photo[] #relation(fields: [photoIds], references: [id])
photoIds String[] #db.ObjectId
...
}
I am able to create photos and posts just fine, but the problem is when I try to create a new post that connects the existing photos. This is my query:
return await prisma.user.update({
where: {id},
data: {
posts: {
create: {
photoIds,
},
}
}
})
This works for the Posts side of the relation, the Post's photoIds are populated correctly, however the Photo's postIds are left empty. I am not sure how to connect the other side of the relation.
The bizarre thing is that when I do a Post count query on the Photo, it returns the correct count number:
await prisma.user.findUnique({where: { id }}).photos({
where: { id: photoId },
select: {
...
_count: {
select: {
posts: true,
}
},
}
})
This query returns the correct post count for each photo, even though it's postIds list is empty. There must be something simple I am missing here, I have read the Prisma docs on many-to-many relations for MongoDB over and over again but can't seem to figure it out.
The secret sauce here is the "include" fields when you create or update.
Instead of updating the user type, just create the photos with the correct userId and the "include" field and it'll automatically link them, such that when you query for users, it'll have attached photos:
Instead of:
return await prisma.user.update({
where: {id},
data: {
posts: {
create: {
photoIds,
},
}
}
});
Use:
return await prisma.photo.create({
data: {
...your photo data
userId: '123'
},
include: {
user: true
}
})

Prisma How to automatically update "updatedAt" field of parent element when a child element is created or updated?

Let's say I have this schema:
model User {
id String #id #default(cuid())
name String
email String
profile Profile?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
model Profile {
id Int #id #default(autoicrement())
bio String?
avatar String?
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
userId String #unique
}
Now what I want to archive is, if a user's profile is updated, for example, a bio field is updated, I want the updatedAt field on User model to automatically reflect that and get updated too to the current timestamp.
Any guide, hint or suggestion will be much appreciated!!
The easiest way would be to make some wrapper function I think, for example:
const updateUserProfile = (id, data) => {
return prisma.profile.update({
where: {
id
},
data: {
...data,
user: {
update: {
updatedAt: new Date()
}
}
}
})
}

Prisma upsertmany issue Provided List<Json>, expected SimilarCreateWithoutMovieInput

using "#prisma/client": "^2.25.0"
on prisma.upsertOneMovie. Provided List<Json>, expected SimilarCreateWithoutMovieInput or SimilarCreateWithoutMovieInput or SimilarUncheckedCreateWithoutMovieInput or SimilarUncheckedCreateWithoutMovieInput:
[0] type SimilarCreateWithoutMovieInput {
[0] id: Int
[0] backdrop_path: String
[0] title: String
[0] name: String
[0] release_date: DateTime
[0] overview: String
[0] show: TVShowCreateNestedOneWithoutSimilarInput
[0] }
code
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
...
let result = await apiRequest
let similar = result.similar.results.map(similar_movie => {
return {
id: similar_movie.id,
backdrop_path: similar_movie.backdrop_path,
title: similar_movie.title,
name: similar_movie.name,
release_date: similar_movie.release_date,
overview: similar_movie.overview,
movieId: result.id
}
})
const movie = {
id: result.id,
...
similar: {
upsert: similar
}
}
const upsertMovie = await prisma.movie.upsert({
where: { id: movie.id },
update: movie,
create: movie,
})
here is the schema.prisma
model Movie {
id Int #id
...other fields and attributes...
similar Similar[]
}
model Similar {
id Int #id #default(autoincrement())
tmdb_id Int #unique
backdrop_path String
title String?
name String?
release_date String
overview String
movie Movie? #relation(fields: [movie_id], references: [id])
show TVShow? #relation(fields: [show_id], references: [id])
movie_id Int?
show_id Int?
}
Similar is supposed to be an array [] of other Movies nested in a specific Movie object, cant be itself
I do not have experience with prisma upsert on generated types and I am getting the above error. Im expecting to upsert a Movie and at the same time upsert multiple records of Similar that are related to Movie.
i have tried using connectOrCreate but it does not support creating multiple records of similar while creating one record of movie as expected
how do I achieve that?
resources
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#nested-writes
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries#connect-or-create-a-record
Issue was that prisma createMany is not available on sqlite, and on the postgresql connector, it is.
const movie = {
...result,
similar: {
connectOrCreate: result.similar.map(s => ({
create: s,
where: { id: s.id },
}))
}
}
const upsertMovie = await prisma.movie.upsert({
where: { id: movie.id },
update: movie,
create: movie,
})

Resources