prisma query existing item for failed unique key constraint - prisma

I have a form where clients can register for contests. They submit their name and email address, and the registration gets stored in a table in the database which has a composite primary key of the contestId and the clientId.
If someone submits the form a second time (i.e., tries to register for the same contest a second time), Prisma throws a failed constraint error. That's fine, and even expected, but I'd like to return the existing entry, and am having a hard time constructing the query.
Schema:
model Client {
id Int #id #default(autoincrement())
email String #unique
first String?
last String?
registrations Registration[]
}
model Contest {
id Int #id #default(autoincrement())
name String #unique
registrations Registration[]
}
model Registration {
contest Contest #relation(fields: [contestId], references: [id])
contestId Int
contestant Client #relation(fields: [clientId], references: [id])
clientId Int
points Int
##id ([contestId, clientId])
}
Registration:
try {
const registration = await prisma.registration.create({
data: {
points: 1,
contest: {
connectOrCreate: {
where: {
name: contest,
},
create: {
name: contest,
}
}
},
contestant: {
connectOrCreate: {
where: {
email: email,
},
create: {
first: first,
last: last,
email: email,
},
},
},
}
});
return res.status(201).send({ ...registration});
}
For new registrants that works, but if the registration already exists, I end up in the catch block. I assume this is the right way to do this — as opposed to, say, querying for existence first, because that seems expensive given that it's likely to be very rare that someone accidentally tries to re-register — but if this isn't a best practice on how to handle things, I'm open to other suggestions.
In the catch block, I then need to find the existing entry, but neither of the two things I've tried have worked:
catch (error) {
// entry already exists
if ('P2002' === error.code) {
const registration = await prisma.registration.findUnique({
where: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
})
return res.status(200).send(...registration);
}
return res.status(500).end(`Register for contest errorr: ${error.message}`);
}
complains Argument where of type RegistrationWhereUniqueInput needs exactly one argument, but you provided contest and contestant.
And
const registration = await prisma.registration.findUnique({
where: {
contestId_clientId: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
},
})
complains
Unknown arg `contest` in where.contestId_clientId.contest for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Unknown arg `contestant` in where.contestId_clientId.contestant for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Argument contestId for where.contestId_clientId.contestId is missing.
Argument clientId for where.contestId_clientId.clientId is missing.
I feel like this latter approach of using Prisma's auto-generated contestId_clientId is directionally right, but how do I construct the Prisma query to find it, starting from having the contest name and client email?

Instead of finding a unique record from the registration model, you can do a query something like this to find the Contest and Client details. The only valid argument for findUnique in the registration model would be: contestId_clientId and you can only pass contestId and clientId in them, other values are not allowed.
const registration = await prisma.client.findUnique({
where: {
email: 'email',
},
include: {
registrations: {
select: {
contest: contest,
points: true,
},
},
},
});

Related

graphql with prisma use where in update func with relation field

schema.prisma file I need help how can I update a post based on 2 conditions? The user id that I get from userId which belongs to user tab and post id which belongs to on other post table
updatePost: async (parant, args, context) => {
const { userId } = context;
const postExist = await context.prisma.post.findUnique({
where: {
id: args.id,
author:{
id: userId
}
}
})
if (!postExist) {
throw new Error ('post not found')
}
const updatedPost = await context.prisma.post.update({
data:{
...args.data
}
})
return updatedPost
},
error code:
"message": "\nInvalid `context.prisma.post.findUnique()` invocation
Argument where of type PostWhereUniqueInput needs exactly one argument, but you provided id and author. Please choose one. Available args: ",
"type PostWhereUniqueInput {",
" id?: Int",
"Unknown arg `author` in where.author for type PostWhereUniqueInput. Available args:",
"",
"type PostWhereUniqueInput {",
" id?: Int",
"}",
You should use context.prisma.post.findFirst()
context.prisma.post.findUnique will only accept fields with #unique field in your schema file. In this case, that is only your Post id.

Missing field in available args

I encountered a problem while trying to seed the user table.
I have a one-to-one relationship between the User and UserSettings model in it.
async function generateUsers() {
const users = [];
for (let i = 0; i < randomUsersCount; i++) {
users[i] = {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
email: faker.internet.email(),
password: await hashPassword('testtest'),
phone: faker.phone.phoneNumber('###-###-###'),
role: 'USER',
is_blocked: false,
user_settings : {
create: {
language: 'PL',
color: faker.internet.color(),
}
}
};
}
await prisma.user.createMany({
data: users,
});
}
Error message:
Unknown arg `user_settings` in data.0.user_settings for type UserCreateManyInput. Available args:
type UserCreateManyInput {
id?: Int
first_name: String
last_name: String
email: String
phone: String
password: String
role: UserRoles
is_blocked?: Boolean
created_at?: DateTime
updated_at?: DateTime
}
but in my model the field exists:
model User {
id Int #id #default(autoincrement())
first_name String
last_name String
email String #unique
phone String
password String
role UserRoles
is_blocked Boolean #default(false)
created_at DateTime #default(now())
updated_at DateTime #updatedAt
user_settings UserSettings?
}
model UserSettings {
id Int #id #default(autoincrement())
language Languages
color String
user_id Int
user User #relation(fields: [user_id], references: [id])
}
When I turn on Prisma Studio, I can see the column present in the table.
Where did I go wrong?
I have run npx prisma generate and npx prisma migrate and even npx prisma migrate reset many times, without success.
You are trying to use nest createMany which isn't supported.
This is from the documentation:
You cannot create or connect relations - you cannot nest create,
createMany, connect, connectOrCreate inside a top-level createMany
As an alternative I would suggest to create users inside the loop while you are creating data.
async function generateUsers() {
const users = [];
for (let i = 0; i < randomUsersCount; i++) {
users[i] = {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
email: faker.internet.email(),
password: await hashPassword('testtest'),
phone: faker.phone.phoneNumber('###-###-###'),
role: 'USER',
is_blocked: false,
user_settings : {
create: {
language: 'PL',
color: faker.internet.color(),
}
}
};
await prisma.user.create({
data: users[i],
});
}
}

Relation type in Model is not defined

I am new to Prisma.
I am trying to use Prisma with Nextjs.
But I am facing problems with one to many relations.
I have a schema like below.
model Thread {
id Int #id #default(autoincrement()) #unique
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #default(autoincrement()) #unique
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
// Kakiko means "Post" in Japanese.
There is "one to many Relation" which towards "kakiko" from Thread.
I can get datas from DB and I can send datas to browser, But My TS compiler says "TS2339: Property 'kakikoes' does not exist on type 'Thread'."
await prisma.thread.findUnique({
where: {
id: threadId
},
include: {
kakikoes: true
}
})
{thread.kakikoes.map(kakiko => <p>{kakiko.body}</p>)}
I checked type definition, and kakikoes not defined.
/**
* Model Thread
*
*/
export type Thread = {
id: number
itaId: number
title: string
}
How can I solve this?
Thank you.
Added (2022/04/03)
I use Intelij Idea Ultimate.
Type is defined like this.
My code is working correctly on My browser And Nextjs dev Tool doesnt say Any Errors nor Any Warnings.
The model is correct. I have tried it and this is the working solution.
schema.prisma file
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Thread {
id Int #id #unique #default(autoincrement())
title String
kakikoes Kakiko[]
}
model Kakiko {
id Int #id #unique #default(autoincrement())
thread Thread #relation(fields: [threadId], references: [id])
threadId Int
}
main.ts file
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient();
async function main() {
// ... you will write your Prisma Client queries here
await prisma.kakiko.create({
data: {
thread: {
create: {
title: 'test',
},
},
},
});
const thread = await prisma.thread.findUnique({
where: {
id: 1,
},
include: {
kakikoes: true,
},
});
console.log('thread', thread);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Here is the response
thread { id: 1, title: 'test', kakikoes: [ { id: 1, threadId: 1 } ] }
Attaching the image as well:
I would also suggest restarting your vs code typescript compiler which could cause this issue.

Postman GET request in order to retrieve a Mongodb entry by _id

I'm trying to build a postman GET request in order to retrieve an entry that I have in a MongoDB by using the unique id generated in the database.
To be more precise, I am interested in writing a GET request to retrieve for example the next entry :
{
"id": "61a51cacdfb9ea1bd9395874",
"Name": "asdsd",
"Code": "asdca",
"Weight": 23,
"Price": 23,
"Color": "sfd",
"isDeleted": false
}
Does anyone have an idea how to include that id in the GET request in order to retrieve the prodcut from above?
Thanks!
EDIT :
#J.F thank you for the kind response and information provided, but unforunately, it still does not work :(.
Those are the products that I have right now, and I tried to GET the one with id = 61a51cacdfb9ea1bd9395874
And this is the response I got :
Also, this is the logic that I implemented for the GET request :
filename : product.service.ts
async getSingleProduct(productId: string) {
const product = await this.findProduct(productId);
return {
id: product.id,
Name: product.Name,
Code: product.Code,
Weight: product.Weight,
Price: product.Price,
Color: product.Price,
isDeleted: product.isDeleted };
}
private async findProduct(id: string): Promise<Product> {
let product;
try {
const product = await this.productModel.findById(id)
} catch (error) {
throw new NotFoundException('Product not found');
}
if (!product) {
throw new NotFoundException('Product not found');
}
return product;
}
filename : product.controller.ts
#Get(':id')
getProduct(#Param('id') prodId: string) {
return this.productsService.getSingleProduct(prodId)
}
EDIT2 :
#Controller('produse')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
#Post()
async addProduct(
#Body('Name') prodName: string,
#Body('Code') prodCode: string,
#Body('Weight') prodWeight: number,
#Body('Price') prodPrice: number,
#Body('Color') prodColor: string,
#Body('isDeleted') prodIsDeleted: boolean,
) {
const generatedId = await this.productsService.createProduct(
prodName,
prodCode,
prodWeight,
prodPrice,
prodColor,
prodIsDeleted
);
return { id: generatedId };
To implement a RESTful API your endpoints has to be like this:
Verb
Path
GET
/resource
GET
/resource/:id
POST
/resource
PUT
/resource/:id
DELETE
/resource/:id
The path you want is GET /resource/:id
The id is used into route because is unique and identify a resource.
So your path can be something like:
http://localhost:8080/v1/resource/61a51cacdfb9ea1bd9395874

Unknown argument error when creating record

I'm encountering some interesting behaviour when using Prisma ORM. It is related to Prisma's generated types, and I've been skimming the docs trying to find out more, but there doesn't seem to be much info about generated types in there (please correct me if I'm mistaken). Here's the behaviour:
Say I have a model with two 1-1 relations (Profile in the example below):
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int #id #default(autoincrement())
name String
profile Profile?
}
model Profile {
id Int #id #default(autoincrement())
name String
userId Int?
user User? #relation(fields: [userId], references: [id])
photoId Int?
photo Photo? #relation(fields: [photoId], references: [id])
}
model Photo {
id Int #id #default(autoincrement())
url String
profile Profile?
}
The following code works when creating a new profile:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
user: { connect: { id: user.id } },
photo: { create: { url: "http://example.com/img" } },
},
});
... but this fails with an error:
const user = await prisma.user.create({ data: { name: "TestUser" } });
const profile = await prisma.profile.create({
data: {
name: "TestProfile",
userId: user.id,
photo: { create: { url: "http://example.com/img" } },
},
});
The error is:
Unknown arg userId in data.userId for type ProfileCreateInput. Did you mean user? Available args:
type ProfileCreateInput {
  name: String
  user?: UserCreateNestedOneWithoutProfileInput
  photo?: PhotoCreateNestedOneWithoutProfileInput
}
Why is the second create-profile code invalid?
Prisma essentially generates two type definitions for a create query. This is implemented with a XOR type, which ensures that only one definition out of two is fully specified and passed to the query:
export type ProfileCreateArgs = {
/* ... */
data: XOR<ProfileCreateInput, ProfileUncheckedCreateInput>;
}
The definitions are called checked and unchecked, the former using nested fields and the latter using raw ids:
export type ProfileCreateInput = {
id?: number;
/* ... */
user?: UserCreateNestedOneWithoutProfileInput;
photo?: PhotoCreateNestedOneWithoutProfileInput;
}
export type ProfileUncheckedCreateInput = {
id?: number;
/* ... */
userId?: number;
photoId?: number;
}
Which basically means that you either provide all references as connect, create etc. relations (checked) or as raw ids (unchecked). You cannot mix the styles, this is not supported.