Related
So I was trying to create a Pinterest Clone using GraphQL and I am stuck with this error.
Project Structure looks something like this
There are 2 models, User and Pins
Pin Model
const mongoose = require('mongoose');
const pinSchema = new mongoose.Schema({
title: {
type: String,
required: [true, 'Title is Required'],
unique: [true, 'Title should be unique'],
},
imageUrl: {
type: String,
required: [true, 'Image URL is Required'],
},
description: {
type: String,
},
link: {
type: String,
},
userId: {
type: mongoose.Schema.Types.ObjectId, // to store which user
required: true,
},
createdAt: {
type: Date,
default: Date.now(),
},
});
const Pin = mongoose.model('Pin', pinSchema);
module.exports = Pin;
User Model
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Name is Required'],
},
userName: {
type: String,
required: [true, 'Username is Required'],
unique: [true, 'Username should be unique'],
},
email: {
type: String,
required: [true, 'Email is Required'],
unique: [true, 'Email should be unique'],
},
password: {
type: String,
required: [true, 'Password is Required'],
},
createdPins: {
type: [mongoose.Schema.Types.ObjectId], // store all pins created by this user
},
savedPins: {
type: [mongoose.Schema.Types.ObjectId], // store all pins saved by this user
},
});
const User = mongoose.model('User', userSchema);
module.exports = User;
Graphql PinType
const { GraphQLObjectType, GraphQLID, GraphQLString } = require('graphql');
const User = require('../models/UserModel');
const PinType = new GraphQLObjectType({
name: 'Pin',
fields: () => ({
id: { type: GraphQLID },
title: { type: GraphQLString },
imageUrl: { type: GraphQLString },
description: { type: GraphQLString },
link: { type: GraphQLString },
user: {
type: UserType,
resolve(parent, args) {
return User.findById(parent.userId);
},
},
createdAt: { type: String },
}),
});
module.exports = PinType;
const UserType = require('./UserSchema');
Graphql UserType
const {
GraphQLObjectType,
GraphQLID,
GraphQLString,
GraphQLList,
} = require('graphql');
const UserType = new GraphQLObjectType({
name: 'User',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
userName: { type: GraphQLString },
createdPins: { type: new GraphQLList(PinType) },
savedPins: { type: new GraphQLList(PinType) },
}),
});
module.exports = UserType;
const PinType = require('./PinSchema');
Query and Mutation: schema.js file
const {
GraphQLObjectType,
GraphQLList,
GraphQLID,
GraphQLSchema,
GraphQLNonNull,
GraphQLString,
} = require('graphql');
const User = require('../models/UserModel');
const Pin = require('../models/PinModel');
const UserType = require('./UserSchema');
const PinType = require('./PinSchema');
// Query
const RootQuery = new GraphQLObjectType({
name: 'RootQuery',
fields: {
// Get all Users
users: {
type: new GraphQLList(UserType),
resolve(parent, args) {
return User.find();
},
},
// Get a Single User
user: {
type: UserType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return User.findById(args.id);
},
},
// Get all Pins
pins: {
type: new GraphQLList(PinType),
resolve(parent, args) {
return Pin.find();
},
},
// Get a Single Pin
pin: {
type: PinType,
args: { id: { type: GraphQLID } },
resolve(parent, args) {
return Pin.findById(args.id);
},
},
},
});
// Mutation
const Mutation = new GraphQLObjectType({
name: 'Mutation',
fields: {
// Create User
createUser: {
type: UserType,
args: {
name: { type: new GraphQLNonNull(GraphQLString) },
userName: { type: new GraphQLNonNull(GraphQLString) },
email: { type: new GraphQLNonNull(GraphQLString) },
password: { type: new GraphQLNonNull(GraphQLString) },
},
resolve(parent, args) {
return User.create({
name: args.name,
userName: args.userName,
email: args.email,
password: args.password,
});
},
},
// Delete User
deleteUser: {
type: UserType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve(parent, args) {
// delete all pins created by this user
Pin.find({ userId: args.id }).then((pins) => {
pins.forEach((pin) => {
pin.remove();
});
});
return User.findByIdAndRemove(args.id);
},
},
// Create a Pin
createPin: {
type: PinType,
args: {
title: { type: new GraphQLNonNull(GraphQLString) },
imageUrl: { type: new GraphQLNonNull(GraphQLString) },
description: { type: GraphQLString },
link: { type: GraphQLString },
userId: { type: new GraphQLNonNull(GraphQLID) },
},
resolve(parent, args) {
return Pin.create({
title: args.title,
imageUrl: args.imageUrl,
description: args.description,
link: args.link,
userId: args.userId,
});
},
},
// Update a Pin
updatePin: {
type: PinType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
title: { type: GraphQLString },
imageUrl: { type: GraphQLString },
description: { type: GraphQLString },
link: { type: GraphQLString },
},
resolve(parent, args) {
return Pin.findByIdAndUpdate(
args.id,
{
$set: {
title: args.title,
imageUrl: args.imageUrl,
description: args.description,
link: args.link,
},
},
{ new: true }
);
},
},
// Delete a Pin
deletePin: {
type: PinType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve(parent, args) {
// remove this pin from the createdPins of the user
User.updateMany(
{},
{
$pullAll: {
createdPins: [args.id],
},
}
);
// delete this pin
return Pin.findByIdAndRemove(args.id);
},
},
},
});
const schema = new GraphQLSchema({
query: RootQuery,
mutation: Mutation,
});
module.exports = schema;
Getting this error
Error: Schema must contain uniquely named types but contains multiple types named "String".
at new GraphQLSchema (D:\Projects\Pinterest Clone\server\node_modules\graphql\type\schema.js:219:15)
at Object.<anonymous> (D:\Projects\Pinterest Clone\server\schemas\schema.js:159:16)
at Module._compile (node:internal/modules/cjs/loader:1149:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1203:10)
at Module.load (node:internal/modules/cjs/loader:1027:32)
at Module._load (node:internal/modules/cjs/loader:868:12)
at Module.require (node:internal/modules/cjs/loader:1051:19)
at require (node:internal/modules/cjs/helpers:103:18)
at Object.<anonymous> (D:\Projects\Pinterest Clone\server\index.js:6:16)
at Module._compile (node:internal/modules/cjs/loader:1149:14)
Node.js v18.10.0
Tried to search for this and found many people faced similar kind of issues, but I couldn't solve it.
Found the Bug. It was in the Graphql PinType file. While defining the schema, I used "String" instead of "GraphqlString" in the field "createdAt" which was causing the error.
I face an issue that i can't resolve alone.
I have a MongoDB collection, in this collection i have 1 document atm.
When i use .findById(_id) or .findOne({_id : id}) with the right _id, everything works.
When i use .findById(_id) or .findOne({_id : id}) with the wrong _id (for test purposes), i have no response (no undefined, no null, nothing) from the DB and my request keep running.
Ty for your time, take care !
EDIT :
Document :
export interface OrderDocument extends mongoose.Document {
user: UserDocument['_id'];
asset_bought: AssetDocument['_id'];
asset_b_symbol: string;
asset_sold: AssetDocument['_id'];
asset_s_symbol: string;
exchange: ExchangeDocument['_id'];
exchange_name: string;
is_draft: Boolean;
amount: number;
atm_price: number;
date: Date;
}
Collection's schema :
const orderSchema = new mongoose.Schema(
{
_id: { type: mongoose.Schema.Types.ObjectId },
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
},
asset_bought: {
type: mongoose.Schema.Types.ObjectId,
ref: 'assets',
required: true,
},
asset_b_symbol: {
type: String,
required: true,
},
asset_sold: {
type: mongoose.Schema.Types.ObjectId,
ref: 'assets',
required: true,
},
asset_s_symbol: {
type: String,
required: true,
},
exchange: {
type: mongoose.Schema.Types.ObjectId,
ref: 'exchanges',
required: true,
},
exchange_name: {
type: String,
required: true,
},
is_draft: { type: Boolean, default: false },
amount: { type: Number, required: true },
atm_price: { type: Number, required: true },
date: { type: Date, required: true },
},
{ timestamps: true }
);
Service
export async function findAndPopulateOrders(
searchType: 'id' | 'one' | 'many',
query: FilterQuery<OrderDocument>,
_collections: Array<string>
) {
const collections = _collections.join(' ');
if (searchType === 'id') {
return await OrderModel.findById(query).populate(collections);
} else if (searchType === 'one') {
return await OrderModel.findOne(query).populate(collections);
} else if (searchType === 'many') {
return await OrderModel.find(query).populate(collections);
} else {
return {};
}
}
This is my user model code. I am referencing the favorites using dynamic referencing as there are three types of posts that can be added to favorites
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
contact:{
type:Number,
required: true,
unique:true
},
cnic: {
type: Number,
required: true,
unique: true
},
favorites: [
{
postType: {
type: String
},
postId: {
type: mongoose.Schema.Types.ObjectId,
refPath: 'postType'
},
}
],
itemsRented: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rent',
}
],
itemsRentedOut: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rent',
}
],
collectionRequestsSent: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'CommunityService',
}
],
itemsCollected: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'CommunityService',
}
],
servicesOrdered: [
{
type:mongoose.Schema.Types.ObjectId,
ref: 'Services'
}
],
paymentDetails: {
card: { type: Number },
cvc: { type: Number },
name: { type: String },
email: {type: String },
expiryDate: {
month: { type: Number },
year: { type: Number }
},
},
address: { type: String },
isDisputeResolutionStaff: {
type: String,
default: false
}
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
next()
}
const salt = await bcrypt.genSalt(10)
this.password = await bcrypt.hash(this.password, salt)
})
const User = mongoose.model('User', userSchema)
export default User
This is my controller. I want to get the posts' details added to favorites by using populate but when I run the code it returns the user object not the post details
const getFavorites = asyncHandler(async(req,res) => {
await User.
findById("61b51adfb7b8a64fd87420d3").
populate("favorites").
exec(function (err, story) {
if (err) throw new Error(err);
console.log(story);
});
})
I am creating multi vendor ecommerce platform, with the following schema.
var user = new Schema(
{
uid: { type: String, index: true, unique: true },
firstName: { type: String, required: true, default: null },
lastName: { type: String, default: null, default: null },
userEmail: { type: String, unique: true, required: true, lowercase: true, },
userProfileImg: { type: String, required: true, default: null },
userDesignation: { type: String, default: null },
userMobile: { type: Number, required: true, default: null },
products: { type: Schema.Types.ObjectId, ref: 'Product' },
}
);
var product = new Schema(
{
sku: { type: String, required: true, unique: true },
title: { type: String, required: true },
category: { type: Array, default: [] },
images: { type: Array, default: [], },
groups: { type: Array, default: [], },
price: { type: Number, default: null, },
unit: { type: String, default: null, },
quantity: { type: Number, default: null, },
description: { type: String, default: null, },
},
);
var AllUser = mongoose.model('User', user, 'AllUsers');
var Allproducts = mongoose.model('Product', product, 'AllProducts');
how can i save multiple products while referring to multiple users? Later i want to populate products based on the users.
Your problem is in referencing the collection. In here when you compile your models
var AllUser = mongoose.model('User', user, 'AllUsers');
var Allproducts = mongoose.model('Product', product, 'AllProducts');
you use Product and for database collection you use AllProducts. That's the problem so...try doing it like this
var Users = mongoose.model('Users', user, 'Users');
var Products = mongoose.model('Products', product, 'Products');
Give it a proper naming convention.
Also there is s typo here in this code.. here I have fixed it
var product = new Schema(
{
sku: { type: String, required: true, unique: true },
title: { type: String, required: true },
category: { type: Array, default: [] },
images: { type: Array, default: [] },
groups: { type: Array, default: [] },
price: { type: Number, default: null },
unit: { type: String, default: null },
quantity: { type: Number, default: null },
description: { type: String, default: null}
}
);
also in your user schema
var user = new Schema(
{
uid: { type: String, index: true, unique: true },
firstName: { type: String, required: true, default: null },
lastName: { type: String, default: null, default: null },
userEmail: { type: String, unique: true, required: true, lowercase: true,
},
userProfileImg: { type: String, required: true, default: null },
userDesignation: { type: String, default: null },
userMobile: { type: Number, required: true, default: null },
products: [{ type: Schema.Types.ObjectId, ref: 'Product' }]
}
);
make products as an array type so that you can store multiple product ids
I have this User model:
const userSchema = new Schema({
_id: {
type: Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
notification: {
experiment_id: {
type: Schema.Types.ObjectId,
ref: "Experiment",
required: false
},
seen: {
type: Boolean,
required: true,
default: false
}
}
});
And this Experiment model:
const experimentSchema = new Schema(
{
_id: {
type: Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
description: {
type: String,
required: true,
default: "No Description"
},
author_id: {
type: Schema.Types.ObjectId,
ref: "User",
required: true
}
);
I am trying to populate from User the experiment_id in notification.
And from this populate, I would like to populate the author_id as well.
I have seen some code like I have done below but I didn't succeed.
I am trying this:
User.find(
{
_id: req.params.currentUserId
},
"notification"
)
.populate({ path: "experiment_id", populate: { path: "author_id" } })
.exec((err, notif) => {
});
I fixed it by adding notification.experiment_id in the path
User.find(
{
_id: req.params.currentUserId
},
"notification"
)
.populate({ path: "notification.experiment_id", populate: { path: "author_id" } })
.exec((err, notif) => {
});