How can I create a nested item in the DB? - postgresql

I am having several problems when I try to create a new user with a nested related table.
My schema is like this:
model User {
id Int #id #default(autoincrement())
username String #unique
email String #unique
avatar String #default("none")
role Role #default(USER)
password String
maxScore Int #default(0)
UserCategoryStats UserCategoryStats[]
}
model UserCategoryStats {
id Int #id #default(autoincrement())
user User #relation(fields: [userId], references: [id])
userId Int
category Category #relation(fields: [categoryId], references: [id])
categoryId Int
correctAnswers Int #default(0)
incorrectAnswers Int #default(0)
totalAnswers Int #default(0)
timesChosen Int #default(0)
}
And I am just trying to create an user with one(or more) UserCategoryStats like the docs say
Like this:
const newUser = await prisma.user.create({
data: {
username,
email,
password: hashedPassword,
UserCategoryStats: {
create: { //Here is the error
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0,
},
},
},
include: {
UserCategoryStats: true,
},
});
This is giving me this Typescript error :
Type '{ correctAnswers: number; incorrectAnswers: number; totalAnswers: number; timesChosen: number; }' is not assignable to type '(Without<UserCategoryStatsCreateWithoutUserInput, UserCategoryStatsUncheckedCreateWithoutUserInput> & UserCategoryStatsUncheckedCreateWithoutUserInput) | ... 5 more ... | undefined'.
Type '{ correctAnswers: number; incorrectAnswers: number; totalAnswers: number; timesChosen: number; }' is not assignable to type 'undefined'
I have tried with multiple values but I simply dont know what is wrong. I am not able to create nested properties.
What am I doing wrong?

Considering the Prisma Query and Schema that you have shared there were no typescript errors, it can be that your PrismaClient is not in sync with the schema, you can execute npx prisma generate to verify if both are in sync.
Here's a working example:
main.ts
import { PrismaClient } from '#prisma/client';
const prisma = new PrismaClient({
log: ['query'],
});
async function main() {
const newUser = await prisma.user.create({
data: {
username: 'alice',
email: 'alice#prisma.io',
password: 'Prisma#111',
UserCategoryStats: {
create: {
//Here is the error
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0,
},
},
},
include: {
UserCategoryStats: true,
},
});
console.log(newUser);
}
main()
.catch((e) => {
throw e;
})
.finally(async () => {
await prisma.$disconnect();
});
Response:
> ts-node index.ts
prisma:query BEGIN
prisma:query INSERT INTO "public"."User" ("username","email","avatar","password","maxScore") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."User"."id"
prisma:query INSERT INTO "public"."UserCategoryStats" ("userId","correctAnswers","incorrectAnswers","totalAnswers","timesChosen") VALUES ($1,$2,$3,$4,$5) RETURNING "public"."UserCategoryStats"."id"
prisma:query SELECT "public"."User"."id", "public"."User"."username", "public"."User"."email", "public"."User"."avatar", "public"."User"."password", "public"."User"."maxScore" FROM "public"."User" WHERE "public"."User"."id" = $1 LIMIT $2 OFFSET $3
prisma:query SELECT "public"."UserCategoryStats"."id", "public"."UserCategoryStats"."userId", "public"."UserCategoryStats"."correctAnswers", "public"."UserCategoryStats"."incorrectAnswers", "public"."UserCategoryStats"."totalAnswers", "public"."UserCategoryStats"."timesChosen" FROM "public"."UserCategoryStats" WHERE "public"."UserCategoryStats"."userId" IN ($1) OFFSET $2
prisma:query COMMIT
{
id: 1,
username: 'alice',
email: 'alice#prisma.io',
avatar: 'none',
password: 'Prisma#111',
maxScore: 0,
UserCategoryStats: [
{
id: 1,
userId: 1,
correctAnswers: 0,
incorrectAnswers: 0,
totalAnswers: 0,
timesChosen: 0
}
]
}
Inserted Data:

Related

crud for a many to many relationship in prisma

so, i want to create a memory for a user and add the user to the memory api using prisma. in this user table and memory table are connected by a many to many relationship. They share a common table userMemory.
schema.prisma
model User {
userId String #id #default(uuid())
avatarUrl String?
bio String?
firstName String
lastName String?
dob DateTime
createdAt DateTime #default(now())
modifiedAt DateTime #default(now()) #updatedAt
createdBy String?
modifiedBy String?
followersCount BigInt #default(0)
followingCount BigInt #default(0)
memories UserMemory[]
}
model Memory {
memoryId String #id #default(uuid())
users UserMemory[]
latitude Float?
longitude Float?
createdAt DateTime #default(now())
modifiedAt DateTime #default(now()) #updatedAt
createdBy String?
modifiedBy String?
textHtml String?
mediaUrl String[]
}
This is my user services file
import { Injectable } from '#nestjs/common';
import { GraphQLError } from 'graphql/error';
import { PrismaService } from 'src/prisma/prisma.service';
import {
CreateUserInput,
OrderByInput,
UpdateUserInput,
} from 'src/types/graphql';
#Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}
async create(createUserInput: CreateUserInput) {
try {
let user = await this.prisma.user.create({
data: createUserInput,
});
user = await this.prisma.user.update({
where: { userId: user.userId },
data: {
createdBy: user.userId,
modifiedBy: user.userId,
},
});
return user;
} catch (e) {
throw new GraphQLError('Error Occurred', {
extensions: { e },
});
}
}
}
This is my memories service file
import { Injectable } from '#nestjs/common';
import { GraphQLError } from 'graphql';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreateMemoryInput, UpdateMemoryInput } from 'src/types/graphql';
#Injectable()
export class MemoriesService {
constructor(private prisma: PrismaService) {}
async create({ authorId, ...createMemoryInput }: CreateMemoryInput) {
try {
const user = await this.prisma.user.findUnique({
where: {
userId: authorId,
},
});
const memory = await this.prisma.memory.create({
data: {
...createMemoryInput,
createdBy: authorId,
modifiedBy: authorId,
users: {
create: [
{
user: {
connect: {
userId: authorId,
...user,
},
},
},
],
},
},
include: {
users: true,
},
});
return memory;
} catch (e) {
throw new GraphQLError('Error Occurred', {
extensions: { e },
});
}
}
async findOne(memoryId: string) {
try {
return await this.prisma.memory.findUnique({
where: {
memoryId,
},
include: {
users: true,
},
});
} catch (e) {
throw new GraphQLError('Error Occurred', {
extensions: { e },
});
}
}
}
my users type defination file
scalar Date
scalar URL
scalar Timestamp
scalar UUID
type User {
userId: UUID!
avatarUrl: URL
bio: String
firstName: String!
lastName: String
dob: Date
createdAt: Timestamp
modifiedAt: Timestamp
createdBy: UUID
modifiedBy: UUID
memories: [Memory]
}
input CreateUserInput {
firstName: String!
lastName: String
dob: Date!
}
type Mutation {
createUser(createUserInput: CreateUserInput!): User!
}
my memories type defination file
type Memory {
memoryId: UUID!
users: [User]
latitude: Float
longitude: Float
createdAt: Timestamp
modifiedAt: Timestamp
createdBy: UUID
modifiedBy: UUID
textHtml: String
mediaUrl: [String]
}
input CreateMemoryInput {
authorId: UUID!
latitude: Float
longitude: Float
textHtml: String
mediaUrl: [String]
}
type Query {
memories: [Memory]!
memory(memoryId: UUID!): Memory
}
type Mutation {
createMemory(createMemoryInput: CreateMemoryInput!): Memory!
}
this is my creat mutation query
mutation CreateMemory($createMemoryInput: CreateMemoryInput!) {
createMemory(createMemoryInput: $createMemoryInput) {
createdAt
createdBy
latitude
longitude
mediaUrl
memoryId
modifiedAt
modifiedBy
textHtml
users {
accountType
avatarUrl
bio
createdAt
createdBy
dob
firstName
gender
lastName
modifiedAt
modifiedBy
userId
}
}
}
so whenever i am creating a memoory against a user, i am getting this error, although data is population in data base.
{
"errors": [
{
"message": "Cannot return null for non-nullable field User.firstName.",
"locations": [
{
"line": 43,
"column": 7
}
],
"path": [
"memory",
"users",
0,
"firstName"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Cannot return null for non-nullable field User.firstName.",
" at completeValue (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:594:13)",
" at executeField (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:489:19)",
" at executeFields (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:413:20)",
" at completeObjectValue (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:914:10)",
" at completeValue (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:635:12)",
" at /Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:696:25",
" at Function.from (<anonymous>)",
" at completeListValue (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:676:34)",
" at completeValue (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:607:12)",
" at executeField (/Users/sohamnandi/Desktop/Projects/link-yatri-api/node_modules/graphql/execution/execute.js:489:19)"
]
}
}
}
],
"data": {
"memory": {
"createdAt": 1674566005576,
"createdBy": "5a147521-a797-4587-b75f-a07f5b00430e",
"latitude": null,
"longitude": null,
"mediaUrl": [],
"memoryId": "0f2a1a5a-298b-4b8c-a526-7e06e0d4b126",
"modifiedAt": 1674566005576,
"modifiedBy": "5a147521-a797-4587-b75f-a07f5b00430e",
"textHtml": null,
"users": [
null
]
}
}
}

How to connect to more than one relation using Prisma Client upsert function?

When I'm trying to connect more than one relation to my Profile table I get the following error message:
PrismaClientValidationError:
Invalid `prisma.connection.upsert()` invocation:
{
where: {
id: ''
},
update: {
userOneId: 'clbcb6z58000gyb31ez95frvh',
userTwoId: 'clbcaz4bl000ayb31icwpzphu',
isUserOneApproved: false,
isUserTwoApproved: true,
connectionRequestedOnDate: '2022-12-07T21:48:12.441Z',
connectionAcceptedOnDate: undefined
},
create: {
userOneId: 'clbcb6z58000gyb31ez95frvh',
~~~~~~~~~~~~~
userTwoId: 'clbcaz4bl000ayb31icwpzphu',
~~~~~~~~
isUserOneApproved: false,
isUserTwoApproved: true,
connectionRequestedOnDate: '2022-12-07T21:48:12.441Z',
connectionAcceptedOnDate: undefined,
userOne: {
connect: {
id: 'clbcb6z58000gyb31ez95frvh'
}
},
userTwo: {
connect: {
id: 'clbcaz4bl000ayb31icwpzphu'
}
},
profile: {
connect: {
id: 'clbcb71l2000kyb31cclk79z3'
}
}
}
}
Unknown arg `userOneId` in create.userOneId for type ConnectionCreateInput. Did you mean `userOne`? Available args:
type ConnectionCreateInput {
id?: String
isUserOneApproved: Boolean
isUserTwoApproved: Boolean
connectionRequestedOnDate?: DateTime | Null
connectionAcceptedOnDate?: DateTime | Null
userOne: ProfileCreateNestedOneWithoutUserOneInput
userTwo: ProfileCreateNestedOneWithoutUserTwoInput
profile: ProfileCreateNestedOneWithoutConnectionInput
}
Unknown arg `userTwoId` in create.userTwoId for type ConnectionCreateInput. Did you mean `userTwo`? Available args:
type ConnectionCreateInput {
id?: String
isUserOneApproved: Boolean
isUserTwoApproved: Boolean
connectionRequestedOnDate?: DateTime | Null
connectionAcceptedOnDate?: DateTime | Null
userOne: ProfileCreateNestedOneWithoutUserOneInput
userTwo: ProfileCreateNestedOneWithoutUserTwoInput
profile: ProfileCreateNestedOneWithoutConnectionInput
}
This is how my prisma upsert function is currently defined:
const createOrUpdateRole = await prisma.connection.upsert({
where: { id: id },
update: {
userOneId: userOneId,
userTwoId: userTwoId,
isUserOneApproved,
isUserTwoApproved,
connectionRequestedOnDate,
connectionAcceptedOnDate,
},
create: {
userOneId: userOneId,
userTwoId: userTwoId,
isUserOneApproved,
isUserTwoApproved,
connectionRequestedOnDate,
connectionAcceptedOnDate,
userOne: { connect: { id: userOneId } },
userTwo: { connect: { id: userTwoId } },
profile: { connect: { id: profileId } },
},
});
My expectation when running the function was to connect userOne and userTwo to my Profile table using their respective ids. The reason for that was that I could then query for the connection and then read the profile data for userOne and userTwo.
This is my models defined in schema.prisma:
model Profile {
id String #id #default(cuid())
firstName String?
lastName String?
image String? #db.Text
userId String
user User #relation(fields: [userId], references: [id], onDelete: Cascade)
sensitive Sensitive[]
userOne Connection[] #relation("userOne")
userTwo Connection[] #relation("userTwo")
connection Connection[] #relation("profile")
##index([userId])
}
model Connection {
id String #id #default(cuid())
isUserOneApproved Boolean
isUserTwoApproved Boolean
connectionRequestedOnDate DateTime?
connectionAcceptedOnDate DateTime?
userOneId String
userOne Profile #relation("userOne", fields: [userOneId], references: [id], onDelete: Cascade)
userTwoId String
userTwo Profile #relation("userTwo", fields: [userTwoId], references: [id], onDelete: Cascade)
profileId String
profile Profile #relation("profile", fields: [profileId], references: [id], onDelete: Cascade)
##index([userOneId])
##index([userTwoId])
##index([profileId])
}
If I decide to one connect to only one of them, for instance userOneId using userOne: { connect: { id: userOneId } }, then the function runs as expected, but as soon as I start to define more than one I get the error mentioned above. What have I missed?
The error is pretty self-descriptive:
Unknown arg `userOneId` in create.userOneId for type ConnectionCreateInput
Since you are already passing userOne: {connect: {id: '.....' } } in your Prisma operation, you do not (and should not) also pass userOneId. You just need userOne: {connect..... and userTwo: {connect......
Your operation will end up looking like this:
const createOrUpdateRole = await prisma.connection.upsert({
where: {
id: id
},
update: {
// You can have __either__ `userOneId: 'value'` or `userOne: {connect....` here
// but not both
userOneId: userOneId,
userTwoId: userTwoId,
isUserOneApproved,
isUserTwoApproved,
connectionRequestedOnDate,
connectionAcceptedOnDate,
},
create: {
isUserOneApproved,
isUserTwoApproved,
connectionRequestedOnDate,
connectionAcceptedOnDate,
userOne: {
connect: {
id: userOneId
}
},
userTwo: {
connect: {
id: userTwoId
}
},
profile: {
connect: {
id: profileId
}
},
},
});

Prisma : Models and Relationship 1-n

I have two tables User and Tasks and a user can have many tasks, however i want a query to return a particular task, fetching details for the task, with author and assigned to users from the user table, usually would be done using aliases. DB is mysql - Thanks
//schema.prisma
model User {
id Int #id #default(autoincrement())
taskby Task[] #relation("taskBy")
taskto Task[] #relation("taskTo")
}
model Task {
id Int #id #default(autoincrement())
created_at DateTime #default(now())
updated_at DateTime #updatedAt
assigned_to_uid Int
assigned_by_uid Int
assigned_to User #relation("taskTo",fields: [assigned_to_uid], references: [id])
assigned_by User #relation("taskBy",fields: [assigned_by_uid], references: [id])
}
API:
if (id) {
res = await prisma.task.findUnique({
where: { id },
include: {
assigned_to: true
},
include: {
assigned_by: true
},
})
} else {...
Desired Response:
{
"id": 2,
"taskid": 2,
"assigned_to_uid": 1,
"assigned_by_uid": 2,
"assigned_by": {
"id": 2,
"firstName": "user2",
},
"assigned_to": {
"id": 1
"firstName": "user1",
},
}
You should be able to get the desired response by using the below query:
if (id) {
const response = await prisma.task.findUnique({
where: { id },
include: {
assigned_to: true,
assigned_by: true,
},
});
console.log(response);
}
Response for the above query:
{
id: 1,
created_at: 2022-02-28T07:22:06.917Z,
updated_at: 2022-02-28T07:22:06.918Z,
assigned_to_uid: 2,
assigned_by_uid: 1,
assigned_to: { id: 2, firstName: 'Jane', lastName: 'Doe' },
assigned_by: { id: 1, firstName: 'John', lastName: 'Doe' }
}

How to connect a many to many relationship using Prisma

I am trying to create and connect a record in a Prisma many to many relationship, but the connection part is not working for me.
Here are my Prisma models:
model Ingredient {
id Int #id #default(autoincrement())
name String
createdAt DateTime #default(now())
calories Int
protein Int
fat Int
carbs Int
netCarbs Int
metricQuantity Int
metricUnit String
imperialQuantity Int
imperialUnit String
recipes IngredientsOnRecipes[]
categories CategoriesOnIngredients[]
}
model IngredientCategory {
id Int #id #default(autoincrement())
name String
ingredients CategoriesOnIngredients[]
}
model CategoriesOnIngredients {
ingredient Ingredient #relation(fields: [ingredientId], references: [id])
ingredientId Int // relation scalar field (used in the `#relation` attribute above)
ingredientCategory IngredientCategory #relation(fields: [ingredientCategoryId], references: [id])
ingredientCategoryId Int // relation scalar field (used in the `#relation` attribute above)
assignedAt DateTime #default(now())
##id([ingredientId, ingredientCategoryId])
}
Here is the primsa query I am running:
const ingredient = await prisma.ingredient.create({
data: {
name: title,
metricQuantity: parseInt(quantityMetric),
metricUnit: unitMetric,
imperialQuantity: parseInt(quantityImperial),
imperialUnit: unitImperial,
calories: parseInt(calories),
netCarbs: parseInt(netCarbs),
carbs: parseInt(carbs),
protein: parseInt(protein),
fat: parseInt(fat),
categories: {
ingredientcategory: {
connect: { id: parseInt(categoryId) },
},
},
},
});
Creating a new ingredient works perfectly, but when I add this section:
categories: {
ingredientcategory: {
connect: { id: parseInt(categoryId) },
},
},
I get the following error:
Unknown arg ingredientcategory in data.categories.ingredientcategory for type CategoriesOnIngredientsCreateNestedManyWithoutIngredientInput. Did you mean createMany? Available args:
type CategoriesOnIngredientsCreateNestedManyWithoutIngredientInput {
create?: CategoriesOnIngredientsCreateWithoutIngredientInput | List | CategoriesOnIngredientsUncheckedCreateWithoutIngredientInput | List
connectOrCreate?: CategoriesOnIngredientsCreateOrConnectWithoutIngredientInput | List
createMany?: CategoriesOnIngredientsCreateManyIngredientInputEnvelope
connect?: CategoriesOnIngredientsWhereUniqueInput | List
}
You can try executing the following:
const { PrismaClient } = require('#prisma/client')
const prisma = new PrismaClient()
const saveData = async () => {
const ingredient = await prisma.ingredient.create({
data: {
name: 'ingredient1',
categories: {
create: {
ingredientCategory: {
create: {
name: 'category1',
},
}
}
},
},
select: {
id: true,
name: true,
categories: {
select: {
ingredientId: true,
ingredientCategory: true,
}
},
},
});
console.log(JSON.stringify(ingredient, null, 2));
}
saveData()
And you will have the following:
I managed to get it to work, I had missed create:{} from my Prisma query.
const ingredient = await prisma.ingredient.create({
data: {
name: title,
metricQuantity: parseInt(quantityMetric),
metricUnit: unitMetric,
imperialQuantity: parseInt(quantityImperial),
imperialUnit: unitImperial,
calories: parseInt(calories),
netCarbs: parseInt(netCarbs),
carbs: parseInt(carbs),
protein: parseInt(protein),
fat: parseInt(fat),
categories: {
create: {
ingredientCategory: {
connect: { id: parseInt(categoryId) },
},
},
},
},
});

findUnique query returns null for array fields

I read the Prisma Relations documentation and it fixed my findMany query which is able to return valid data but I'm getting inconsistent results with findUnique.
Schema
model User {
id Int #id #default(autoincrement())
fname String
lname String
email String
password String
vehicles Vehicle[]
}
model Vehicle {
id Int #id #default(autoincrement())
vin String #unique
model String
make String
drivers User[]
}
Typedefs
const typeDefs = gql'
type User {
id: ID!
fname: String
lname: String
email: String
password: String
vehicles: [Vehicle]
}
type Vehicle {
id: ID!
vin: String
model: String
make: String
drivers: [User]
}
type Mutation {
post(id: ID!, fname: String!, lname: String!): User
}
type Query {
users: [User]
user(id: ID!): User
vehicles: [Vehicle]
vehicle(vin: String): Vehicle
}
'
This one works
users: async (_, __, context) => {
return context.prisma.user.findMany({
include: { vehicles: true}
})
},
However, for some reason the findUnique version will not resolve the array field for "vehicles"
This one doesn't work
user: async (_, args, context) => {
const id = +args.id
return context.prisma.user.findUnique({ where: {id} },
include: { vehicles: true}
)
},
This is what it returns
{
"data": {
"user": {
"id": "1",
"fname": "Jess",
"lname": "Potato",
"vehicles": null
}
}
}
I was reading about fragments and trying to find documentation on graphql resolvers but I haven't found anything relevant that can solve this issue.
Any insight would be appreciated! Thanks!
You need to fix the arguments passed to findUnique. Notice the arrangement of the { and }.
Change
return context.prisma.user.findUnique({ where: { id } },
// ^
include: { vehicles: true}
)
to
return context.prisma.user.findUnique({
where: { id },
include: { vehicles: true }
})