How to made a relation to user model using Prisma? - database-schema

I'm trying to query comments and the user who made the comments. I'm using Prisma to create the schema and Planetscale as a database.
Below you find part of my schema
model User {
id String #id #default(cuid())
name String?
email String? #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
Comment Comment[]
}
model Comment {
id String #id #default(cuid())
text String
contentId String
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
commenterId String?
commenter User? #relation(fields: [commenterId], references: [id])
}
This is my API route:
import type { NextApiRequest, NextApiResponse } from "next";
import prisma from "../../../lib/prisma";
export default async function handle(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "GET") {
return res.status(405).json({ msg: "Method not allowed" });
}
try {
const comments = await prisma.comment.findMany();
return res.status(200).json(comments);
} catch (err) {
console.error(err);
res.status(500).json({ msg: "Something went wrong." });
}
res.end();
}
The end goals is to query the comments and get an object with the commenter, so I can display name and image.
How should my #relation in the model Comment look like to make it possible to include the commenter in the query?

You need to tell prisma if you want to include a related model like this:
await prisma.comment.findmany({
include: {
commenter: true,
},
})
https://www.prisma.io/docs/concepts/components/prisma-client/relation-queries

Related

Prisma: how to delete a record where it may or may not have children

I am trying to figure out how to delete a record created in my graphql, apollo, prisma app, where the record I am deleting may have a has many relationship to another model.
I have a model called IssueGroups. IssueGroups have many issues.
I have a model with:
import * as Prisma from "#prisma/client"
import { Field, ObjectType } from "type-graphql"
import { BaseModel } from "../shared/base.model"
#ObjectType()
export class IssueGroup extends BaseModel implements Prisma.IssueGroup {
#Field()
title: string
#Field()
description: string
#Field(type => String)
issues: Prisma.Issue[];
}
A resolver with:
import { Arg, Mutation, Query, Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
import { IssueGroupService } from "./issueGroup.service"
import { IssueGroupInput } from "./inputs/create.input"
import { Inject, Service } from "typedi"
#Service()
#Resolver(() => IssueGroup)
export default class IssueGroupResolver {
#Inject(() => IssueGroupService)
issueGroupService: IssueGroupService
#Query(() => [IssueGroup])
async allIssueGroups() {
return await this.issueGroupService.getAllIssueGroups()
}
#Query(() => IssueGroup)
async issueGroup(#Arg("id") id: string) {
return await this.issueGroupService.getIssueGroup(id)
}
#Mutation(() => IssueGroup)
async createIssueGroup(#Arg("data") data: IssueGroupInput) {
return await this.issueGroupService.createIssueGroup(data)
}
// : Promise<IssueGroup[]> {
#Mutation(() => IssueGroup)
async updateIssueGroup(
#Arg("id") id: string,
#Arg("data") data: IssueGroupInput
) {
return await this.issueGroupService.updateIssueGroup(id, data)
}
#Mutation(() => IssueGroup)
async deleteIssueGroup(#Arg("id") id: string) {
return await this.issueGroupService.deleteIssueGroup(id)
}
}
// private readonly issueGroupService: IssueGroupService
// #Query(() => [IssueGroup])
// async issueGroups(): Promise<IssueGroup[]> {
// return this.issueGroupService.findAll()
// }
// #Query(() => IssueGroup)
// async issueGroup(#Arg("id") id: string): Promise<IssueGroup> {
// return this.issueGroupService.findOne(id)
// }
// #Mutation(() => IssueGroup)
// async createIssueGroup(
// #Arg("data") data: IssueGroupInput
// ): Promise<IssueGroup> {
// return this.issueGroupService.create(data)
// }
A service with:
import { prisma } from "../../lib/prisma"
import { Service } from "typedi"
import { IssueGroupInput } from "./inputs/create.input"
import { Resolver } from "type-graphql"
import { IssueGroup } from "./issueGroup.model"
#Service()
#Resolver(() => IssueGroup)
export class IssueGroupService {
async createIssueGroup(data: IssueGroupInput) {
return await prisma.issueGroup.create({
data,
})
}
async deleteIssueGroup(id: string) {
return await prisma.issueGroup.delete({ where: { id } })
}
async updateIssueGroup(id: string, data: IssueGroupInput) {
const issueGroup = await prisma.issueGroup.findUnique({ where: { id } })
if (!issueGroup) {
throw new Error("Issue not found")
}
return await prisma.issueGroup.update({ where: { id }, data })
}
async getAllIssueGroups() {
return (await prisma.issueGroup.findMany({orderBy: {title: 'asc'}}))
}
async getIssueGroup(id: string) {
return await prisma.issueGroup.findUnique({
where: {
id,
},
})
}
}
When I try to delete the issueGroup (which does not currently have any issues, I can see an error that points to issue.delete and says:
operation failed because it depends on one or more records that were
required but not found. Record to delete does not exist.
In my form I have:
import * as React from "react"
import { gql } from "#apollo/client"
import type { IssueGroupInput } from "lib/graphql"
import { QueryMode, Role, SortOrder, useAllIssueGroupsQuery, useDeleteIssueGroupMutation } from "lib/graphql"
const __ = gql`
query AllIssueGroups {
allIssueGroups {
id
title
description
}
}
mutation deleteIssueGroup($id: String!) {
deleteIssueGroup(id: $id) {
id
title
description
issues // I have tried both with and without issues in this list
}
}
`
export default function IssueGroups() {
const [deleteIssueGroup] = useDeleteIssueGroupMutation()
const { data, loading, refetch } = useAllIssueGroupsQuery({
fetchPolicy: "cache-and-network",
})
const allIssueGroups = data?.allIssueGroups
const onDeleteIssueGroup = (id: string) => {
return (
deleteIssueGroup({ variables: { id } }).then(() => refetch())
)
}
return (
<List>
{data?.allIssueGroups.map((issueGroup) => (
<ListItem key={issueGroup.id}>
{issueGroup.title}{issueGroup.description}
<Button onClick={() => onDeleteIssueGroup(issueGroup.id)}>Delete</Button>
</ListItem>
))}
</List>
)
}
I have seen this page of the prisma documentation and I think I have followed its advice for how to deal with an issue when the parent issueGroup is deleted.
model IssueGroup {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issues Issue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
model Issue {
id String #id #default(dbgenerated("gen_random_uuid()")) #db.Uuid
title String
description String
issueGroup IssueGroup #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String #db.Uuid
subscribers UserIssue[]
createdAt DateTime #default(now()) #db.Timestamptz(6)
updatedAt DateTime #default(now()) #updatedAt #db.Timestamptz(6)
}
However, VS code thinks this is an error. It gives me an error message that says:
The onDelete referential action of a relation should not be set to
SetNull when a referenced field is required. We recommend either to
choose another referential action, or to make the referenced fields
optional.
My db is psql, which the prisma docs suggest, should allow SetNull
PostgreSQL PostgreSQL is the only database supported by Prisma that
allows you to define a SetNull referential action that refers to a
non-nullable field. However, this raises a foreign key constraint
error when the action is triggered at runtime.
For this reason, when you set postgres as the database provider in the
(default) foreignKeys relation mode, Prisma warns users to mark as
optional any fields that are included in a #relation attribute with a
SetNull referential action. For all other database providers, Prisma
rejects the schema with a validation error.
I can see from this page of the docs that lists cannot be optional, so I dont think I can follow the advice in the relational page of the docs and make the issueGroup issues[] as optional.
How can i delete the issue (if there were any) at the same time as I delete the issue group?
Just make issueGroup optional
model IssueGroup {
...
issues Issue[]
...
}
model Issue {
...
issueGroup IssueGroup? #relation(fields: [issueGroupId], references: [id], onDelete: SetNull, onUpdate: Cascade)
issueGroupId String? #db.Uuid
...
}
The actual message from prisma is saying that
Prisma warns users to mark as optional any fields that are included in
a #relation attribute with a SetNull referential action
Your relationship has onDelete: SetNull - so mark it as optional

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

Unknown error getting started with Prisma

I've got an odd error, I've been googling to no end and have been unable to figure this out.
I've got a prisma.schema looking like this;
// 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 Account {
id String #id #default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? #db.Text
access_token String? #db.Text
expires_at Int?
token_type String?
scope String?
id_token String? #db.Text
session_state String?
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
##unique([provider, providerAccountId])
}
model Session {
id String #id #default(cuid())
sessionToken String #unique
userId String
expires DateTime
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
}
model User {
id String #id #default(cuid())
name String?
email String #unique
emailVerified DateTime?
image String?
accounts Account[]
sessions Session[]
}
model VerificationToken {
identifier String
token String #unique
expires DateTime
##unique([identifier, token])
}
and a addUser.ts file looking like this...
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import { User, PrismaClient } from '#prisma/client';
import type { NextApiRequest, NextApiResponse } from 'next';
type Data = {
name: string;
};
const prisma = new PrismaClient();
export default async(req: NextApiRequest, res: NextApiResponse) => {
const createUser: User = await prisma.user.create({
data:{
name: `${Math.random() * 10000}`,
email: `${Math.random() * 10000}#gmail.com`,
}
});
res.json('user created succesfully');
}
Trouble is when I run the code I get this error....
Error:
Invalid `prisma.user.create()` invocation:
{
data: {
name: '8096.003938770688',
email: '8343.394432092025#gmail.com'
~~~~~
}
}
Unknown arg `email` in data.email for type UserCreateInput. Did you mean `name`? Available args:
type UserCreateInput {
name: String
}
But if I look at the UserCreateInput type (I haven't made any changes to this file, I've used prisma generate to match my schema when I've made changes to it) I can see this....
export type UserCreateInput = {
id?: string
name?: string | null
email: string
emailVerified?: Date | string | null
image?: string | null
accounts?: AccountCreateNestedManyWithoutUserInput
sessions?: SessionCreateNestedManyWithoutUserInput
}
If I comment out the line email: ${Math.random() * 10000}#gmail.com, and set the schema to "email String? #unique" it works fine and creates the entry into the DB as expected, so it seems to be something with my email field but I can't figure out what.
I have been following multiple guides but none of them seem to expect this issue or have any resolutions for it.
I should point out, I am fairly new to development, and I'm not a developer by trade, so this is more of a learning thing for me :)
Appreciate your time guys

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.

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.