Prisma query returns null, when data exists - node.js

I've built a Nestjs app with Prisma and GQL. I just changed computer (from a Mac to PC) and all of a sudden my login mutation seems to have stopped working, literally the only one... Here is some code:
auth.resolver.ts:
#Mutation((returns) => AuthenticatedUser)
async login(
#Context() { res }: Auth.GqlContext,
#Args('payload', { type: () => UserLoginDto }) payload: UserLoginDto
): Promise<AuthenticatedUser> {
const authenticatedUser = await this.authService.login(payload)
res.cookie('jwt', authenticatedUser.jwt, cookieConfig)
return authenticatedUser
}
auth.service.ts:
public async login(payload: UserLoginDto): Promise<AuthenticatedUser> {
const { password, email } = payload
Logger.log({ password, email })
const user = await this.usersService.getUser(email) // returns null since the change
Logger.log(JSON.stringify(user))
const isValidPassword = await this.verifyPassword(user.password, password) // error is thrown here
if (!isValidPassword) {
throw new AuthenticationError('Password does not match')
}
const { id } = user
const jwt = await this.jwtService.signAsync(
{ id, email },
{
secret: process.env.JWT_SECRET
}
)
return {
jwt,
user
}
}
users.service.ts:
public async getUser(email: string): Promise<User | null> {
const { user } = this.prismaService
Logger.log(email) // returns 'email#gmail.com'
const temp = user.findUnique({ where: { email } }) // returns null here
Logger.log(`PRISMA USER: ${JSON.stringify(temp)}`) // null
return temp
}
What's weird is that I have the same queries set up for posts and categories, but those work fine. Nothing to do with JWT either.
My user is in the DB too:
Full GQL mutation:
mutation {
login(payload: { email: "email#gmail.com", password: "123" }) {
jwt
user {
name
id
email
}
}
}
GQL error:
{
"errors": [
{
"message": "Cannot read property 'password' of null",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"login"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'password' of null",
" at AuthService.login (H:\\work\\blog-api\\src\\auth\\auth.service.ts:25:60)",
" at AuthResolver.login (H:\\work\\blog-api\\src\\auth\\auth.resolver.ts:43:31)",
" at target (H:\\work\\blog-api\\node_modules\\#nestjs\\core\\helpers\\external-context-creator.js:77:28)",
" at H:\\work\\blog-api\\node_modules\\#nestjs\\core\\helpers\\external-proxy.js:9:24"
]
}
}
}
],
"data": null
}
Prisma schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Post {
id String #id #default(uuid())
title String
slug String #unique
description String
relativeImage String #map("relative_image")
rawMdx String #map("raw_mdx")
published Boolean #default(true)
views Int #default(0)
likes Int #default(0)
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
categories Category[]
}
model Category {
id String #id #default(uuid())
name String #unique
color String
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
posts Post[]
}
model User {
id String #id #default(uuid())
name String #unique
email String #unique
password String
createdAt DateTime #default(now()) #map("created_at")
updatedAt DateTime #updatedAt #map("updated_at")
}
I've also run npx prisma db pull && npx prisma generate since switching computers...
Update
After about an hour this issue disappeared. If anyone knows why I had this issue, I'll love to know why.

Related

Prisma throws error on findUniqueByEmail request, and it refers for some reason to findUniqueById request

I'm a newbie in Prisma, and I'm confused with the findUniqueOrThrow method, where I'm trying to get user record by unique email value. For some reason, the error I get after sending a request to this endpoint refers to a other findUniqueOrThrow method, but with an id selection. Can someone help, how can if fix it?
schema.prisma
model User {
id Int #id #default(autoincrement())
firstName String?
lastName String?
email String? #unique
password String?
phone String?
roles Role[] #default([CUSTOMER])
isBanned Boolean? #default(false)
comments Comment[]
token String?
createdAt DateTime? #default(now())
updatedAt DateTime? #updatedAt
}
user.service.ts
#Injectable()
export class UserService {
constructor(private prisma: PrismaClient) {}
async findUserByEmail(email: string): Promise<User> {
try {
const user = await this.prisma.user.findUniqueOrThrow({
where: {
email: email,
},
});
return user;
} catch (e) {
console.log(e);
}
user.controller.ts
#Get('user/findByEmail')
async getUserByEmail(#Body() data: EmailDto) {
return await this.userService.findUserByEmail(data.email);
}
Error:
error
Route order in your user controller matters. Make sure you have the GET route to get user by email above the GET route for getting user by id.

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
}

Update parent and upsert child in Prisma

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

How to query Enum using Prisma?

The goal is simple, I want to query enum field in the where clause. Example
prisma version is: "prisma": "3.11.0"
schema.prisma
model Todo {
id Int #default(autoincrement()) #id
title String
content String
status TodoStatus #default(IN_PROGRESS)
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
enum TodoStatus {
IN_PROGRESS
DONE
}
api call
app.get('/todos', (req, res){
const { status } = req.query
const todos = await prisma.todo.findMany({
where: { status: status },
orderBy: {
id: "asc",
},
});
res.json(todos);
})
Frontend I use Next.js and it is a select option
example
<select
id="todo"
name="todo"
onChange={(e) => setSelectedStatus(e.target.value as Status)}
>
{[Status.IN_PROGRESS, Status.DONE].map((status: Status) => {
return (
<option value={status}>{todoStatusToString[status]}</option>
);
})}
</select>
The enum value from Next.js
export enum Status {
IN_PROGRESS = "IN_PROGRESS",
DONE = "DONE",
ALL = "ALL",
}
export const todoStatusToString = {
[Status.IN_PROGRESS]: "In progress",
[Status.DONE]: "Done",
[Status.ALL]: "All",
};
the req.query will be sent from clientside in this format
localhost:3000/todos?status=DONE
{ status: "DONE" }
or
localhost:3000/todos?status=IN_PROGRESS
{ status: "IN_PROGRESS" }
I know that Prisma is built-in to be typesafe. So my assumption is because the data that we get from the frontend is a string type while the enum on Prisma is looking for either IN_PROGRESS or DONE specifically if we send "DECLINED" to status where clause it will spit the same error.
Any help would be appreciated!
In this case you need to cast the type.
You can do it like this
where: { status: status as unknown as TodoStatus}

Resources