Nodejs Prisma include does not respect grandparent id - node.js

I am using prisma orm in nodejs and have the following schema (example)
model User
{
id Int #id #default(autoincrement())
answers Answer[]
}
model Product
{
id Int #id #default(autoincrement())
questions Question[]
}
model Question
{
id Int #id #default(autoincrement())
products Product[]
answers Answer[]
}
model Answer
{
id Int #id #default(autoincrement())
userId Int
user User #relation(fields: [userId], references: [id])
productId Int
product Product #relation(fields: [productId], references: [id])
questionId Int
question Question #relation(fields: [questionId], references: [id])
}
Is it possible to get a list of products with questions and with latest answer for each question?
The problem I have is, that nested includes don't respect grandparent id.
For example, in this code:
let products = await prisma.product.findMany({
include: {
questions: {
include: {
answers: true
}
}
}
})
returned answers for each question don't also belong to the product I want them to belong to.
What am I missing?

To the best of my knowledge, it's not possible to make the query you're looking for (A second layer of nested include that also respects the grandparent ID).
A possible workaround is to include the answers alongside the questions in the product query instead of nesting it inside questions. Then you can merge the questions and answers in the application layer (either in the backend or frontend, whichever is most appropriate).
The code would look something like this
let products = await prisma.product.findMany({
include: {
questions: true,
answers: true, // Assuming the relation field is called answers. You confirmed you accidentally forgot to mention the field in your schema.
},
});
// In Javascript/Typescript: For every product, iterate across every answer and add to the questions array.
This workaround should work reasonably well provided the number of products/answers isn't incredibly large.

Related

Prisma don't let me make a relation to the same table

I'm starting Prisma for a project and I'm making the schema. I have a Customer table, and want to have a "referredBy" column, pointing to a customerId.
model Customer {
id Int #id #default(autoincrement())
uuid String #unique #default(uuid())
referralCode String?
referredBy Customer? #relation(fields: [referredById], references: [id])
referredById Int?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
}
But it gives me an error.
Error validating field referreddBy in model Customer: The relation field referreddBy on Model Customer is missing an opposite relation field on the model Customer. Either run prisma format or add it manually.
It's not the first time I've encountered this. But usually it's for another table relation so I just add the opposite on the other table.
model Account {
id Int #id #default(autoincrement())
uuid String #unique #default(uuid())
**product Product #relation(fields: [productId], references: [id])**
customers Customer[]
}
I add
to the Product model:
Account Account[]
What kind of opposite relation field should I add?
Regards

Prisma client generated type for create input has weird type requirement for related table field, how to get a working type?

Use case is simple:
users table
sessions table
Each user can be logged in multiple places therefore they can have multiple sessions -> one to many relationship. in SQL this isn't a big deal/problem, but the generated types available in #prisma/client create a weird type when it comes to the input models.
I'm using NestJS and need TS models based on classes that exist at runtime, so I can make use of decorators on keys for input validation and graphql usage.
I'm creating a model based off of the Prisma.SessionCreateInput coming from #prisma/client.
prisma schema:
model User {
id String #id #default(uuid())
...
sessions Session[]
}
model Session {
id String #id #default(uuid())
user User #relation(fields: [userId], references: [id])
userId String
}
The TS model:
import { Prisma } from '#prisma/client';
export class SessionModel implements Prisma.SessionCreateInput {
id?: string;
userId?: string;
user: Prisma.UserCreateNestedOneWithoutSessionsInput;
}
Now I'm defining the type for user explicitly based on what Prisma is telling me to, in my opinion I don't need any user data there when creating a session, merely the userId.
UserCreateNestedOneWithoutSessionsInput:
type Prisma.UserCreateNestedOneWithoutSessionsInput = {
create?: (Prisma.Without<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput> & Prisma.UserUncheckedCreateWithoutSessionsInput) | (Prisma.Without<...> & Prisma.UserCreateWithoutSessionsInput);
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput;
connect?: Prisma.UserWhereUniqueInput;
}
which seems some sort of meta type that I very much do not need?
How can I maintain type safety and have these derived TS classes to work with?

Prisma postgres - Unique constraint failed on the fields: (`id`)

For some reason I'm getting the error Unique constraint failed on the fields: (id) when trying to create a new Artist document.
Below is the function I'm calling.
async create(createArtistInput: CreateArtistInput): Promise<Artist> {
console.log(createArtistInput, 'create artist input')
const slug = slugify(createArtistInput.name, {
replacement: '-',
strict: true,
})
return this.db.artist.create({
data: {
name: createArtistInput.name,
spotifyArtistId: createArtistInput.spotifyArtistId,
spotifyArtistName: createArtistInput.spotifyArtistName,
slug,
},
})
}
The console log prints the following response, so I don't understand why the unique constraint of id is failing, as I'm not passing one in. I'm letting the prisma schema handle that.
{
name: 'twofiveone',
spotifyArtistId: '5Fex9xz9rkPqQqMBVtuIrE',
spotifyArtistName: 'twofiveone'
} create artist input
Here is the prisma schema if needed
model Artist {
id Int #id #default(autoincrement())
name String
slug String?
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
spotifyArtistId String?
spotifyArtistName String?
}
Does anyone have any idea what is happening? It's as if I can't create and new artists for some reason.
Autoincrement creates its own sequence starting from 1 which you can read more about here.
If you add random records with the id, then Postgres doesn't know that and it tries to start from 1. If 1 is already present, then it will throw an error.
So the best practice, in this case, is always let the database generate the id for you instead of you adding it manually. Hope this helps
The most likely reason for this is that somehow rows have been manually added with the id 0 at the start.

Prisma client type system creates strong coupling with service methods

I am using prisma ORM with nestjs and it is awesome. Can you please help me understand how can I separate my database layer from my service methods since results produced by prisma client queries are of types generated by prisma client itself ( so i wont be having those types when i shift to lets say typeorm ). how can i prevent such coupling of my service methods returning results of types generated by prisma client and not my custom entities. Hope it makes sense.
The generated #prisma/client library is responsible for generating both the types as well as the custom entity classes. As a result, if you replace Prisma you end up losing both.
Here are two possible workarounds that can decouple the types of your service methods from the Prisma ORM.
Workaround 1: Generate types indepedently of Prisma
With this approach you can get rid of Prisma altogether in the future by manually defining the types for your functions. You can use the types generated by Prisma as reference (or just copy paste them directly). Let me show you an example.
Imagine this is your Prisma Schema.
model Post {
id Int #default(autoincrement()) #id
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
title String #db.VarChar(255)
author User #relation(fields: [authorId], references: [id])
authorId Int
}
model User {
id Int #default(autoincrement()) #id
name String?
posts Post[]
}
You could define a getUserWithPosts function as follows:
// Copied over from '#prisma/client'. Modify as necessary.
type User = {
id: number
name: string | null
}
// Copied over from '#prisma/client'. Modify as necessary.
type Post = {
id: number
createdAt: Date
updatedAt: Date
title: string
authorId: number
}
type UserWithPosts = User & {posts: Post[]}
const prisma = new PrismaClient()
async function getUserWithPosts(userId: number) : Promise<UserWithPosts> {
let user = await prisma.user.findUnique({
where: {
id: userId,
},
include: {
posts: true
}
})
return user;
}
This way, you should be able to get rid of Prisma altogether and replace it with an ORM of your choice. One notable drawback is this approach increases the maintenance burden upon changes to the Prisma schema as you need to manually maintain the types.
Workaround 2: Generate types using Prisma
You could keep Prisma in your codebase simply to generate the #prisma/client and use it for your types. This is possible with the Prisma.validator type that is exposed by the #prisma/client. Code snippet to demonstrate this for the exact same function:
// 1: Define the validator
const userWithPosts = Prisma.validator<Prisma.UserArgs>()({
include: { posts: true },
})
// 2: This type will include a user and all their posts
type UserWithPosts = Prisma.UserGetPayload<typeof userWithPosts>
// function is same as before
async function getUserWithPosts(userId: number): Promise<UserWithPosts> {
let user = await prisma.user.findUnique({
where: {
id: userId,
},
include: {
posts: true
}
})
return user;
}
Additionally, you can always keep the Prisma types updated to your current database state using the Introspect feature. This will work even for changes you have made with other ORMS/Query Builders/SQL.
If you want more details, a lot of what I've mentioned here is touched opon in the Operating against partial structures of your model types concept guide in the Prisma Docs.
Finally, if this dosen't solve your problem, I would request that you open a new issue with the problem and your use case. This really helps us to track and prioritize problems that people are facing.

Prisma2: how to design schema which has relation with Array type Model?

I faced some problems when designing the schema.
case 1:
model User {
package: #relation(fields: [authorId], references: [id])
packageId: Int
}
model Package {
user User[]
}
one package can be subscribed by hundreds of users. So, I think this is the way to go. But, the problem is, when a package needs to be deleted, the user(admin) is also needed to be deleted. which we don't want
case 2:
model User {
package Package[]
}
model package {
author User? #relation(fields: [authorId], references: [id])
authorId Int?
}
by designing this way, when the package is deleted, the user is not deleted.
but I can't connect multiple users to a package.
I think you have to explicitly model the relationship from both sides to get a many-to-many relationship. After all users can subscribe to many packages and packages can have many users subscribed.
model User {
id Int #id #default(autoincrement())
packages Package[] #relation(references: [id])
}
model Package {
id Int #id #default(autoincrement())
user User[] #relation(references: [id])
}

Resources