Query many-to-many relationship in Prisma - prisma

I want to query a many-to-many relation in prisma, like "select all posts where the category ID equals 'abc...' ".
This seems fairly simple but even after spending 2 hours reading the Prisma docs on relational queries, I can't figure it out.
model Category {
id String #id #default(cuid())
name String
post Post[]
}
model Post {
id String #id #default(cuid())
body String
category Category[]
}
const posts = await prisma.post.findMany({
select: {
category: {
where: {id: "abc123"}
}},
});
this returns an array of as many category objects as there are posts.

This will return all posts which have the category of id abc123. Note that posts may include categories other than id abc123.
const posts = await prisma.post.findMany({
where: {
category: {
some: {
id: 'abc123',
},
},
},
});
some: Returns all records where one or more ("some") related records match filtering criteria.
https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#some

Related

1-to-Many relationship not working with Prisma

I'm using Prisma w/ Postgres and am trying to have a simple 1-to-many relationship with a User which can have zero or more Product(s). I'm able to create 1 product for a particular user but when I try to create another product I'm met with this error: Invalid prisma.product.create() invocation: Unique constraint failed on the fields: ('userEmail'). I'm definitely missing something but probably need another pair of eyes on it.
schema.prisma
model Product {
id String #id #default(cuid())
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
name String
description String?
published Boolean #default(false)
user User #relation(fields: [userEmail], references: [email])
userEmail String #unique
productImageUrl String?
}
model User {
id String #id #default(cuid())
name String?
email String? #unique
emailVerified DateTime?
image String?
active Boolean?
products Product[]
}
Next.js API route where I can POST to create new products for a user
const Index = async (_req: NextApiRequest, res: NextApiResponse) => {
const reqBody = _req.body ?? null;
if (!reqBody) res.status(200).json({ message: "No request body found" });
const product = await prisma.product.create({
data: {
user: {
connect: {
email: "xxx#x.com" // this user already exists in the DB
},
},
published: true,
name: "Test Product 3",
createdAt: new Date,
updatedAt: new Date,
}
})
res.status(200).json({ data: reqBody })
};
Silly mistake. All I needed to do was remove #unique from userEmail on the Product model.
you don't need to define foreign key userEmail in the product table as #uniuqe

Apollo GraphQL returns an empty field

I want to query all the products no user has viewed.
The structure is
model Products {
id string
views Views[]
}
model Views {
id string
createdAt DateTime
user User
}
model User {
id string
email string
}
I want to get all the products that no one has viewed. The following query is not working, but I think it should be similar (if this is possible)
query {
Products(where: { views: { is: empty }}) {
id
views
}
}

prisma query existing item for failed unique key constraint

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

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

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.