I am creating an ecommerce api using GraphQL. My prisma schema is
model Product {
id Int #id #default(autoincrement())
createdAt DateTime #default(now())
name String
price Int
stocks Int
}
model User {
id Int #id #default(autoincrement())
name String
email String #unique
password String
Orders Order[]
}
model Order{
id Int #id #default(autoincrement())
product Product #relation(fields: [productId], references: [id])
productId Int
user User #relation(fields: [userId], references: [id])
userId Int
##unique([productId, userId])
}
What should be my graphql schema and resolver function in order to create a relation whenever a user orders a product?
Your schema should actually look like this:
model Product {
id Int #id #default(autoincrement())
name String
price Int
stocks Int
orders Order[]
createdAt DateTime #default(now())
}
model User {
id Int #id #default(autoincrement())
name String
email String #unique
password String
orders Order[]
}
model Order {
id Int #id #default(autoincrement())
product Product #relation(fields: [productId], references: [id])
productId Int
user User #relation(fields: [userId], references: [id])
userId Int
##unique([productId, userId])
}
And your GraphQL mutation should contain the user id and product id being passed as variables and your resolver would look like this:
await prisma.user.update({
where: {
id: 1,
},
data: {
orders: {
create: {
product: {
connect: {
id: 123,
},
},
},
},
},
})
This will create a new order to the user id 1 and will add product id 123 to the order.
Related
i'm currently working on a twitter clone with express / prisma / postgreSQL for my backend.
I'm struggling to imitate twitter's feed.
Here's my current prisma Schema
model User {
id String #id #default(cuid())
email String #unique
username String #unique
profilename String?
password String
bio String?
image String?
createdAt DateTime #default(now())
isAdmin Boolean #default(false)
tweets Tweet[]
retweets Retweet[]
likes Like[]
followers Follows[] #relation("follower")
following Follows[] #relation("following")
}
model Follows {
follower User #relation("following", fields: [followerId], references: [id])
followerId String
following User #relation("follower", fields: [followingId], references: [id])
followingId String
createdAt DateTime #default(now())
##id([followerId, followingId])
}
model Tweet {
id String #id #default(cuid())
content String
createdAt DateTime #default(now())
deleted Boolean #default(false)
media String[]
author User #relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
originalTweet Tweet? #relation("replies", fields: [originalTweetId], references: [id], onDelete: Cascade)
originalTweetId String?
responses Tweet[] #relation("replies")
likes Like[]
retweets Retweet[]
hashtags Hashtag[]
}
model Hashtag {
id String #id #default(cuid())
name String #unique
createdAt DateTime #default(now())
tweets Tweet[]
}
model Retweet {
id String #id #default(cuid())
tweet Tweet #relation(fields: [tweetId], references: [id], onDelete: Cascade)
tweetId String
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime #default(now())
}
model Like {
id String #id #default(cuid())
tweet Tweet #relation(fields: [tweetId], references: [id], onDelete: Cascade)
tweetId String
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
userId String
createdAt DateTime #default(now())
}
and my current function to get user feed
exports.getUserFeed = async (userId, options = {}) => {
try {
const user = await prisma.user.findUnique({
where: { id: userId },
include: { following: true },
})
if (user.following.length === 0) {
return prisma.tweet.findMany({
orderBy: {
createdAt: "desc",
},
...options,
})
}
return prisma.tweet.findMany({
where: {
OR: [
{
author: {
followers: {
some: {
followerId: userId,
},
},
},
},
{
likes: {
some: {
user: {
followers: {
some: {
followerId: userId,
},
},
},
},
},
},
{
retweets: {
some: {
user: {
followers: {
some: {
followerId: userId,
},
},
},
},
},
},
],
},
orderBy: {
createdAt: "desc",
},
...options,
})
} catch (err) {
throw new Error(err)
}
}
With this i get tweets posted by user i follow, liked by user i follow and retweeted by user i follow but order by tweet's created at not by the "event" created at.
To solve this i thought about creating a new model called Event or Status with optionnal feed like:
isRetweet ?
isLike ?
etc
and then retrieve this instead of tweet model.
But it seems messy and complex for no reason.
Is there a way to actually define a better schema or do a better query ?
Thanks a lot.
Hi I have three models
model User {
user_id Int #id #default(autoincrement())
email String #unique
name String?
User_Account User_Account[]
}
model Account {
account_id Int #id #default (autoincrement()) #unique
email String
bank String
createdAt DateTime #default(now())
User_Account User_Account[]
}
model User_Account {
id Int #id #default(autoincrement())
accountId Int
userId Int
User User #relation(fields: [userId], references: [user_id])
Account Account #relation(fields: [accountId], references: [account_id])
}
I am trying to seed my db like this
const data = [
{
id: 1,
email: 'pranit1#mf.com',
name: 'Pranit1',
bank: 'VCB',
ids: [1,1]
},
{
id: 2,
email: 'pranit1#mf.com',
name: 'Pranit1',
bank: 'ACB',
ids: [1,2]
},
{
id: 3,
email: 'pranit3#mf.com',
name: 'Pranit3',
bank: 'VCB',
ids: [2,3]
}
]
const users = await prisma.$transaction(
data.map(user =>
prisma.user.upsert({
where: { email: user.email },
update: {},
create: { name: user.name,
email:user.email },
})
)
);
const accounts = await prisma.$transaction(
data.map(account =>
prisma.account.upsert({
where: { account_id: account.id },
update: {},
create: { bank: account.bank ,
email :account.email },
})
)
);
const user_accounts = await prisma.$transaction(
data.map(uacc =>{
console.log(uacc);
return prisma.user_Account.upsert({
where: { id: uacc.id },
update: {id: uacc.id},
create:{
userId: uacc.ids[0],
accountId: uacc.ids[1] },
})}
)
);
However I am getting an
Unique constraint failed on the constraint: User_Account_userId_key
The data in prisma studio is generated as shown in the image
I am simply trying to create users and accounts and a user can be associated with multiple accounts. Their relation is shown in the User_Account table. I cant see why I am getting a unique constraint error when I dont have the #unique tag on userId
I was unable to reproduce the error on my side.
But I suspect you already had records on the DB and they conflict with the ids of your seeder.
Also, there are some improvements you could make on your schema for being simpler.
Since you don't have any extra details on the many-to-many relation you can get rid of the User_Account model and let Prisma handle it for you.
On the seeder, you can take advantage of the nesting features of Prisma so that you don't have to manually link the records. That way, you don't have to worry about ids.
schema.prisma suggestion
model User {
id Int #id #default(autoincrement())
email String #unique
name String?
accounts Account[]
}
model Account {
id Int #id #unique #default(autoincrement())
email String
bank String
users User[]
createdAt DateTime #default(now())
}
seed.js suggestion
const { PrismaClient } = require("#prisma/client");
const prisma = new PrismaClient();
async function main() {
const usersData = [
{
email: "pranit1#mf.com",
name: "Pranit1",
banks: ["VCB", "ACB"],
},
{
email: "pranit3#mf.com",
name: "Pranit3",
banks: ["VCB"],
},
];
const users = await prisma.$transaction(
usersData.map((user) =>
prisma.user.upsert({
where: { email: user.email },
update: {},
create: {
name: user.name,
email: user.email,
accounts: {
create: user.banks.map((bank) => ({
email: user.email,
bank,
})),
},
},
})
)
);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
I want to find only one post matching by post id and prodCode
below is my query code. It doesn't work.
If i change findUnique to findFirst. it works.
const post = await client.post.findUnique({
where: {
AND: [
{ id: postId },
{
product: {
prodCode,
},
},
],
},
});
prisma model
model Product {
id Int #id #default(autoincrement())
prodName String
prodCode String #unique
posts Post[]
holdings Holding[]
proposes Propose[]
}
model Post {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
userId Int
product Product #relation(fields: [productId], references: [id])
productId Int
title String
content String
createdAt DateTime #default(now())
}
Since Post.id is unique, you don't need to filter by prodCode as well. You could just query the post record with the needed id and then check if the connected product has the right prodCode.
I would just do this:
const post = await prisma.post.findUnique({
where: {
id: postId
},
include: {
product: true
}
});
if (post.product.prodCode === prodCode) {
// No result for desired query
} else {
// "post" variable contains result of desired query
}
So I have this query:
START TRANSACTION;
CREATE TABLE `users` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`registration_date` timestamp NOT NULL,
`address` varchar(255),
`description` varchar(1000),
`studio` varchar(255),
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`is_looking_for_work` boolean,
`last_active` timestamp,
`last_login` timestamp,
`profile_photo` varchar(2000)
);
CREATE TABLE `availability` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`start_time` timestamp NOT NULL,
`end_time` timestamp NOT NULL,
`description` varchar(500),
`user_id` int NOT NULL
);
CREATE TABLE `chats` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`initiator_id` int NOT NULL,
`invited_id` int NOT NULL
);
CREATE TABLE `messages` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`chat_id` int NOT NULL,
`author_id` int NOT NULL,
`content` varchar(1000) NOT NULL,
`creation_date` timestamp NOT NULL
);
CREATE TABLE `offers` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`author_id` int,
`colors` ENUM ('white', 'red'),
`project_image` varchar(10000),
`finished_image` varchar(10000),
`price` int NOT NULL,
`description` varchar(500),
`time_needed` int NOT NULL,
`is_available` boolean NOT NULL,
`is_in_portfolio` boolean
);
CREATE TABLE `meetings` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`provider` int NOT NULL,
`client` int NOT NULL,
`date` timestamp NOT NULL,
`duration` int NOT NULL,
`description` varchar(1000) NOT NULL,
`offer_id` int NOT NULL
);
CREATE TABLE `tags` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`tag` varchar(255) NOT NULL,
`description` varchar(1000),
`is_style` boolean NOT NULL
);
CREATE TABLE `tags_to_offers` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`offer_id` int NOT NULL,
`tag_id` int NOT NULL
);
CREATE TABLE `tags_to_users` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL,
`tag_id` int NOT NULL
);
ALTER TABLE `offers` ADD FOREIGN KEY (`author_id`) REFERENCES `users` (`id`);
ALTER TABLE `availability` ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
ALTER TABLE `chats` ADD FOREIGN KEY (`initiator_id`) REFERENCES `users` (`id`);
ALTER TABLE `chats` ADD FOREIGN KEY (`invited_id`) REFERENCES `users` (`id`);
ALTER TABLE `messages` ADD FOREIGN KEY (`chat_id`) REFERENCES `chats` (`id`);
ALTER TABLE `messages` ADD FOREIGN KEY (`author_id`) REFERENCES `users` (`id`);
ALTER TABLE `tags_to_offers` ADD FOREIGN KEY (`offer_id`) REFERENCES `offers` (`id`);
ALTER TABLE `tags_to_offers` ADD FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`);
ALTER TABLE `tags_to_users` ADD FOREIGN KEY (`user_id`) REFERENCES `users` (`id`);
ALTER TABLE `tags_to_users` ADD FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`);
ALTER TABLE `meetings` ADD FOREIGN KEY (`provider`) REFERENCES `users` (`id`);
ALTER TABLE `meetings` ADD FOREIGN KEY (`client`) REFERENCES `users` (`id`);
ALTER TABLE `meetings` ADD FOREIGN KEY (`offer_id`) REFERENCES `offers` (`id`);
COMMIT;
It causes prisma to create this schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model availability {
id Int #id #default(autoincrement())
start_time DateTime
end_time DateTime
description String?
user_id Int
users users #relation(fields: [user_id], references: [id])
##index([user_id], name: "user_id")
}
model chats {
id Int #id #default(autoincrement())
initiator_id Int
invited_id Int
users_chats_initiator_idTousers users #relation("chats_initiator_idTousers", fields: [initiator_id], references: [id])
users_chats_invited_idTousers users #relation("chats_invited_idTousers", fields: [invited_id], references: [id])
messages messages[]
##index([initiator_id], name: "initiator_id")
##index([invited_id], name: "invited_id")
}
model meetings {
id Int #id #default(autoincrement())
provider Int
client Int
date DateTime
duration Int
description String
offer_id Int
users_meetings_clientTousers users #relation("meetings_clientTousers", fields: [client], references: [id])
offers offers #relation(fields: [offer_id], references: [id])
users_meetings_providerTousers users #relation("meetings_providerTousers", fields: [provider], references: [id])
##index([client], name: "client")
##index([offer_id], name: "offer_id")
##index([provider], name: "provider")
}
model messages {
id Int #id #default(autoincrement())
chat_id Int
author_id Int
content String
creation_date DateTime
users users #relation(fields: [author_id], references: [id])
chats chats #relation(fields: [chat_id], references: [id])
##index([author_id], name: "author_id")
##index([chat_id], name: "chat_id")
}
model offers {
id Int #id #default(autoincrement())
author_id Int?
colors offers_colors?
project_image String?
finished_image String?
price Int
description String?
time_needed Int
is_available Boolean
is_in_portfolio Boolean?
users users? #relation(fields: [author_id], references: [id])
meetings meetings[]
tags_to_offers tags_to_offers[]
##index([author_id], name: "author_id")
}
model tags {
id Int #id #default(autoincrement())
tag String
description String?
is_style Boolean
tags_to_offers tags_to_offers[]
tags_to_users tags_to_users[]
}
model tags_to_offers {
id Int #id #default(autoincrement())
offer_id Int
tag_id Int
offers offers #relation(fields: [offer_id], references: [id])
tags tags #relation(fields: [tag_id], references: [id])
##index([offer_id], name: "offer_id")
##index([tag_id], name: "tag_id")
}
model tags_to_users {
id Int #id #default(autoincrement())
user_id Int
tag_id Int
tags tags #relation(fields: [tag_id], references: [id])
users users #relation(fields: [user_id], references: [id])
##index([tag_id], name: "tag_id")
##index([user_id], name: "user_id")
}
model users {
id Int #id #default(autoincrement())
name String
registration_date DateTime
address String?
description String?
studio String?
email String
password String
is_looking_for_work Boolean?
last_active DateTime?
last_login DateTime?
profile_photo String?
availability availability[]
chats_chats_initiator_idTousers chats[] #relation("chats_initiator_idTousers")
chats_chats_invited_idTousers chats[] #relation("chats_invited_idTousers")
meetings_meetings_clientTousers meetings[] #relation("meetings_clientTousers")
meetings_meetings_providerTousers meetings[] #relation("meetings_providerTousers")
messages messages[]
offers offers[]
tags_to_users tags_to_users[]
}
enum offers_colors {
white
red
}
Which, is not what I was going for. I wanted something like this (GraphQL types):
type User {
id: Int!
name: String!
registration_date: String!
address: String
description: String
studio: String
email: String!
password: String!
is_looking_for_work: Boolean
last_active: String
last_login: String
profile_photo: String
availability: [Availability]
chats: [Chat]
meetings: [Meeting]
tags: [Tag]
}
type Offer {
id: Int!
author_id: Int!
colors: String
project_image: String!
finished_image: String
price: Int!
description: String
time_needed: Int!
is_available: Boolean!
is_in_portfolio: Boolean
tags: [Tag]
}
type Availability {
id: Int!
start_time: String!
end_time: String!
description: String
user_id: Int!
}
type Chat {
id: Int!
initiator_id: Int!
invited_id: Int!
messages: [Message]
}
type Message {
id: Int!
chat_id: Int!
author_id: Int!
content: Int!
creation_date: Int!
}
type Meeting {
id: Int!
providerId: Int!
clientId: Int!
date: String!
duration: Int!
description: String!
offer_id: Int!
}
type Tag {
id: Int!
tag: String!
description: String
is_style: Boolean!
}
For example let's look at user for simplicity:
SQL
CREATE TABLE `users` (
`id` int PRIMARY KEY NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`registration_date` timestamp NOT NULL,
`address` varchar(255),
`description` varchar(1000),
`studio` varchar(255),
`email` varchar(255) NOT NULL,
`password` varchar(255) NOT NULL,
`is_looking_for_work` boolean,
`last_active` timestamp,
`last_login` timestamp,
`profile_photo` varchar(2000)
);
Prisma
model users {
id Int #id #default(autoincrement())
name String
registration_date DateTime
address String?
description String?
studio String?
email String
password String
is_looking_for_work Boolean?
last_active DateTime?
last_login DateTime?
profile_photo String?
availability availability[]
chats_chats_initiator_idTousers chats[] #relation("chats_initiator_idTousers")
chats_chats_invited_idTousers chats[] #relation("chats_invited_idTousers")
meetings_meetings_clientTousers meetings[] #relation("meetings_clientTousers")
meetings_meetings_providerTousers meetings[] #relation("meetings_providerTousers")
// the 4 above: Why are they not in objects?
messages messages[]
offers offers[]
tags_to_users tags_to_users[]
}
What I was hoping for
type User {
id: Int!
name: String!
registration_date: String!
address: String
description: String
studio: String
email: String!
password: String!
is_looking_for_work: Boolean
last_active: String
last_login: String
profile_photo: String
availability: [Availability]
chats: [Chat]
meetings: [Meeting]
// meeting and chats. Why are they not like this?
tags: [Tag]
}
And as you can see
chats_chats_initiator_idTousers chats[] #relation("chats_initiator_idTousers")
chats_chats_invited_idTousers chats[] #relation("chats_invited_idTousers")
meetings_meetings_clientTousers meetings[] #relation("meetings_clientTousers")
meetings_meetings_providerTousers meetings[] #relation("meetings_providerTousers")
This is how prisma made the chats and meethings. Also it added objects. But why did it add the 4 above?
What did I mess up in my schema? Or can I create a db schema from my graphql types?
I couldn't find any relevant info on Prisma docs, nor on SO on this question.
My relevant schema looks like this:
model User {
id Int #default(autoincrement()) #id
createdAt DateTime #default(now())
updatedAt DateTime #updatedAt
firstName String?
lastName String?
email String #unique
hashedPassword String?
role String #default("user")
sessions Session[]
profile Profile?
}
model Profile {
id Int #default(autoincrement()) #id
aboutMe String?
location String?
profession String?
user User #relation(fields:[userId], references: [id])
userId Int
}
I want to update multiple columns in both tables however, have been unable to get the mutation to work in Prisma. So, this is what my mutation looks like at the moment:
...
const {aboutMe, location, profession, firstName, lastName } = inputs;
const profile = await db.user.update({
where: { id: ctx.session!.userId },
data: {
profile: {
update: {
aboutMe,
location,
profession,
},
},
},
});
const user = await db.user.update({
where: { id: ctx.session!.userId },
data: {
firstName,
lastName,
},
});
...
As you can see, I have two mutations, is it possible the update multiple tables with a single mutation?
Thank you.
This should work:
const profile = await db.user.update({
where: { id: u.id },
data: {
firstName: 'name',
lastName: 'name',
profile: {
update: {
aboutMe: 'aboutMe',
location: 'aboutMe',
profession: 'aboutMe',
},
},
},
include: { profile: true },
})