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
]
}
}
}
Related
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
}
},
},
});
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) },
},
},
},
},
});
I am using MongoDB with Mongoose and GraphQL for a class project. I am stuck on an issue with GraphQL returning null on fields within a nested document reference (postedBy which references the User schema). I expect the fields to be populated by the referenced object data, but only the ID returns.
Model
const postSchema = new Schema(
{
postText: {
type: String,
required: 'You need add text to your post',
minlength: 1,
maxlength: 10000,
},
createdAt:{
type: Date,
default: Date.now,
get: createdAtVal => dateFormat(createdAtVal)
},
postedBy: {
type: Schema.Types.ObjectId, ref: "User",
required: true
},
comments: [commentSchema]
},
{
toJSON: {
virtuals: true,
getters: true
},
}
)
postSchema.virtual('commentCount').get(function() {
return this.comments.length;
});
const Post = model('Post', postSchema);
module.exports = Post;
TypeDef
type Post {
_id: ID
postText: String
createdAt: String
postedBy: User
comments: [Comment]
commentCount: Int
}
Resolver
addPost: async (parent, args, context) => {
if (context.user) {
const post = await Post.create({ ...args, postedBy: context.user._id });
await User.findByIdAndUpdate(
{ _id: context.user._id },
{ $push: { posts: post._id } },
{ new: true }
);
return post;
}
throw new AuthenticationError('You need to be logged in!');
}
I am able to successfully query the post and have the referenced field populated with the user's _id, username, and image(url). When I run the mutation, the username and image return null.
Here is my mutation:
mutation addPost($PostText: String!) {
addPost(postText: $postText) {
_id
postText
createdAt
postedBy {
_id
username
image
}
commentCount
comments {
_id
}
}
}
And here is the response it gets:
{
"data": {
"addPost": {
"_id": "60612871bd89e52ca08d3ea1",
"postText": "This is an example of a post.",
"createdAt": "Mar 28th, 2021 at 21:08 pm",
"postedBy": {
"_id": "6060a868d856f01738f45185",
"username": null,
"image": null
},
"commentCount": 0,
"comments": []
}
}
}
What am I doing wrong?
Since you're only getting the ref and no additional data, I think you just forgot to populate the user field.
Try:
return await post.populate('postedBy').execPopulate();
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 }
})
I have the following resolver for GraphQL:
const Post = require("../../models/Post");
module.exports = {
getAllActivePosts: async (userId) => {
try {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
which tries to get all active posts by the ID of the user from the Post model:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema({
userId: {
type: Schema.Types.ObjectId,
ref: "User",
required: true,
},
content: {
type: String,
required: true,
},
createdAt: {
type: Date,
required: true,
}
});
module.exports = Post = mongoose.model("Post", PostSchema);
Here's the GraphQL Schema:
const { buildSchema } = require('graphql');
module.exports = buildSchema(`
type User {
_id: MongoId!
email: String!
password: String
}
type Post {
_id: MongoId!
userId: MongoId!
content: String!
createdAt: String!
}
scalar MongoId
input LoginInput {
email: String!
password: String!
}
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: MongoId!): [Post]
}
type RootMutation {
createUser(loginInput: LoginInput): AuthData!
}
schema {
query: RootQuery
mutation: RootMutation
}
`);
... and the GraphQL query I'm running in GraphiQL:
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
For this, the result of the query is:
{
"errors": [
{
"message": "Cast to ObjectId failed for value \"{ userId: '5fbfc92312b90071179a160f' }\" at path \"userId\" for model \"Post\"",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"getAllActivePosts"
]
}
],
"data": {
"getAllActivePosts": null
}
}
Searched here for similar issues, tried wrapping userId in ObjectId, but nothing helped. What am I missing here?
I was go through this problem once a year ago with no solution till i get main concept of graphql.
Here you are passing string
{
getAllActivePosts(userId: "5fbfc92312b90071179a160f") {
name
createdAt
}
}
and graphql expecting to have mongoose.Types.ObjectId
getAllActivePosts(userId: MongoId!): [Post]
You need to do sync like
getAllActivePosts(userId: mongoose.Types.ObjectId("5fbfc92312b90071179a160f")) {
But using above way you are not eligible for run query in graphiQL becuse there is no mongoose defined.
type RootQuery {
login(email: String!, password: String!): AuthData!
getAllActivePosts(userId: String!): [Post]
}
Better solution is use userId input as string and then validate on your resolver function like
getAllActivePosts: async ({ userId }) => {
try {
if(mongoose.Types.ObjectId.isValid(userId)) {
const posts = await Post.find({
userId: userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
// you can;t return null you need to return array
return posts ? posts : []
} else {
// if mongoose id is wrong
return []
}
} catch(error) {
// it is better to throw error return blank array to complete flow
throw error
}
}
Turned out, I was using userId directly, whereas I should've used args.userId. The proper resolver below:
module.exports = {
getAllActivePosts: async (args) => {
try {
const posts = await Post.find({
userId: args.userId
})
.select(["name", "createdAt"])
.populate("posts", ["name", "createdAt"]);
return posts;
} catch (err) {
console.log(err);
throw err;
}
},
};
and for the schema:
getAllActivePosts(userId: String!): [Post]