I am new to backend development And I am facing some issues with connecting User model with Profile model. And I also want to auto-generate username in profile model when user sets them in User model. I am not sure how to do this. Here is my code
Resolver.js
Mutation: {
signUpUser: async (parent, { userInput }) => {
const { email, username, password } = userInput;
const errors = [];
if (!validator.isEmail(email)) {
errors.push({
message: "Invalid Email",
});
}
const existingUser = await User.findOne({ email: email });
if (existingUser) {
const error = new Error("User exists already");
throw error;
}
if (errors.length > 0) {
const error = new Error("Invalid Input");
error.data = errors;
error.code = 402;
throw error;
}
const hashedPassword = await bcrypt.hash(password, 12);
const user = new User({
email: email,
password: hashedPassword,
username: username,
});
const createdUser = await user.save();
return {
...createdUser._doc,
_id: createdUser._id.toString(),
};
},
UserModel
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new mongoose.Schema(
{
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
username: {
type: String,
required: true,
},
status: {
type: String,
default: "I am new!",
},
posts: [
{
type: Schema.Types.ObjectId,
ref: "Post",
},
],
profile: {
type: Schema.Types.ObjectId,
ref: "Profile",
},
},
{ timestamps: true }
);
module.exports = mongoose.model("User", userSchema);
ProfileModel
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const profileSchema = new mongoose.Schema(
{
firstName: {
type: String,
default: null,
},
lastName: {
type: String,
default: null,
},
bio: {
type: String,
default: null,
},
profilePic: {
type: String,
default: null,
},
username: {
type: String,
default: null,
},
url: {
type: String,
default: null,
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Profile", profileSchema);
Here is the complete code if you like to give it a look
https://github.com/adityakmr7/medium-clone
Any help would be great. Thank you.
I am using Graphql here and apollo.
Related
I'm creating Social Network app using MERN. I have made it so far that users can create posts and have followers and followings. My task is to make an option on the post so it can be public or private. I don't know how to do that. Has anybody idea or example of code how to do it? Thanks!
This is my post model:
const mongoose = require("mongoose");
const postSchema = new mongoose.Schema({
caption: String,
image: {
public_id: String,
url: String,
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
createdAt: {
type: Date,
default: Date.now,
},
likes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
comments: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
comment: {
type: String,
required: true,
},
},
],
visibility: {
type: String,
enum : ["public", "private"],
default: "public"
},
});
module.exports = mongoose.model("Post", postSchema);
This is my user model:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const crypto = require("crypto");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "Please enter a name"],
},
avatar: {
public_id: String,
url: String,
},
email: {
type: String,
required: [true, "Please enter an email"],
unique: [true, "Email already exists"],
},
password: {
type: String,
required: [true, "Please enter a password"],
minlength: [6, "Password must be at least 6 characters"],
select: false,
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
},
],
followers: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
following: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
],
resetPasswordToken: String,
resetPasswordExpire: Date,
});
userSchema.pre("save", async function (next) {
if (this.isModified("password")) {
this.password = await bcrypt.hash(this.password, 10);
}
next();
});
userSchema.methods.matchPassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
userSchema.methods.generateToken = function () {
return jwt.sign({ _id: this._id }, process.env.JWT_SECRET);
};
userSchema.methods.getResetPasswordToken = function () {
const resetToken = crypto.randomBytes(20).toString("hex");
this.resetPasswordToken = crypto
.createHash("sha256")
.update(resetToken)
.digest("hex");
this.resetPasswordExpire = Date.now() + 10 * 60 * 1000;
return resetToken;
};
module.exports = mongoose.model("User", userSchema);
and here is the logic how of creating post:
exports.createPost = async (req, res) => {
try {
const myCloud = await cloudinary.v2.uploader.upload(req.body.image, {
folder: "posts",
});
const newPostData = {
caption: req.body.caption,
image: {
public_id: myCloud.public_id,
url: myCloud.secure_url,
},
owner: req.user._id,
};
const post = await Post.create(newPostData);
const user = await User.findById(req.user._id);
user.posts.unshift(post._id);
await user.save();
res.status(201).json({
success: true,
message: "Post created",
});
} catch (error) {
res.status(500).json({
success: false,
message: error.message,
});
}
};
In your post's model, you can add a "visibility" field with private & public as possible values (one of them default, obviously). Then every time you are querying for that post or list of posts, use that visibility column to decide if the user should be served that post or a 403 response page.
Check this for enum validation.
I try to make one method for signup to two types of user ' buyer and seller ' .
When I save seller , I should get all information about their ' storeinfo ' , see my updated code below .
I used populate() but I get an empty array .
Is my idea wrong ?
code for signup
exports.signupUser = async (req, res, next) => {
role = req.body.role
const user = new User(req.body)
const seller = new Seller(req.body)
try{
if(role=='seller'){
await seller.save()
await user.save()
const token = await user.generateToken()
res.status(200).send({
error: null,
apiStatus:true,
data: {user,seller ,token}
})
}
await user.save()
const token = await user.generateToken()
res.status(200).send({
error: null,
apiStatus:true,
data: {user, token}
})
}
catch(error){
res.status(400).send({
error: error.message,
apiStatus:false,
data: 'unauthorized user'
})
}
}
code for login
exports.login = async (req, res) => {
try{
const user = await User.findUserByCredentials(req.body.email, req.body.password)
const token = await user.generateToken()
console.log(user.role)
if(user.role=='seller'){
console.log(user._id)
await User.findOne({_id: user._id}).populate('storeinfo').exec(function(err, user) {
if (err) {console.log(err)}
res.status(200).send({
error: null,
apiStatus:true,
user,token
})})
}
res.status(200).send({
error: null,
apiStatus:true,
data: {user, token}
})
}
catch(error){
res.status(400).send({
error: error.message,
apiStatus:false,
data: 'Something went wrong'
})
}
}
schema user
const userSchema = new Schema({
first_name: { type: String,required:true},
last_name: { type: String,required:true},
email: { type: String, unique: true, required: true, trim: true,lowercase: true ,validate(value) {
if (!validator.isEmail(value)) throw new Error("Invalid Email"); },
},
password: { type: String,minlength: 6,required: true, trim: true, match: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/},
role: { type: String, enum: ['admin','seller', 'user'], default: 'user' ,required: true},
mobileNumber: { type: String,default:'',trim: true,
validate(value) {
if (!validator.isMobilePhone(value, ["ar-EG"]))
throw new Error("invalid phone number");
},
},
address: {
type: String,
required: true,
trim: true
},
storeinfo:{
type: mongoose.Schema.Types.ObjectId,
ref:'Seller',
},
tokens: [{
token: {
type: String,
required: true
}
}]
}, {
timestamps: true
})
userSchema.methods.generateToken = async function(){
const user = this
const token = jwt.sign({_id: user._id.toString() , role:user.role}, process.env.JWTKEY)
user.tokens = user.tokens.concat({token})
await user.save()
return token
}
// login
userSchema.statics.findUserByCredentials = async(email, password)=>{
const user = await User.findOne({email})
if(!user) throw new Error('invalid email')
const matched = await bcrypt.compare(password, user.password)
if(!matched) throw new Error('invalid password')
return user
}
const User = mongoose.model('User', userSchema)
module.exports = User
seller schema
const SellerSchema = new Schema(
{
storeName: {
required: true,
type: String,
},
category: {
type: String,
required: true
},
image: {
type: String,
// required: true,
},
addresses:[
{
street: String,
locality: String,
aptName: String,
lat: Number,
lng: Number,
}
],
numberOfBranches: Number,
_userId:{ type: Schema.Types.ObjectId, ref: 'User'},
items: [{ type: Schema.Types.ObjectId, ref: "Item" }]
},
{ timestamps: true }
);
const Seller = mongoose.model('Seller', SellerSchema)
module.exports = Seller
I am getting error on console that : Cannot read property 'likes' of null
I am using postman for getting requests and putting response and response.
The array 'likes' is empty and here I am trying to insert the user id inside it but unable to insert it through unshift() method.
This is schema defined in a file Posts.js
const { text } = require('express');
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
avatar: {
type: String
},
likes: [
{
users: {
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
comment: [
{
users: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String,
},
avatar: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Post = mongoose.model('post', PostSchema);
This is a express code for put request in file posts.js
const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator/check');
const auth = require('../../middleware/auth');
const Posts = require('../../models/Posts');
const User = require('../../models/User');
const { route } = require('./profile');
router.put('/like/:id', auth, async(req, res) => {
try {
const post = await Post.findById(req.params.id);
// Check if the post has already been liked
if(post.likes.filter(like => like.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Post already liked' });
}
post.likes.unshift({ user: req.user.id });
await post.save();
res.json(post.likes);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
Here is the typo error I made. In the schema Posts.js in likes and comment array I wrote users instead of user.
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
avatar: {
type: String
},
likes: [
{
users: { // Here it has to be user
type: Schema.Types.ObjectId,
ref: 'users'
}
}
],
comment: [
{
users: { //Here it has to be user
type: Schema.Types.ObjectId,
ref: 'users'
},
I'm creating a web application that has chats, and users can join the chat. Once the user joins the chat, I want to add the user's ID as well as their name to the users field in the Chat schema. So far, I'm able to add their ID, but I am finding it difficult to add their name. Below, I have attached my Chat mongoose model, as well as my route to add a user to a chat. Also, I have attached my User mongoose model. Any help is greatly appreciated. Thank you!
Chat model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ChatSchema = new Schema({
title: {
type: String,
required: true
},
password: {
type: String,
required: true
},
creator: {
type: Schema.Types.ObjectId,
ref: 'user'
},
users: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
name: {
type: String,
required: true
}
}
],
code: {
type: String,
required: true
},
posts: [
{
text: {
type: String,
required: true
},
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Chat = mongoose.model('chat', ChatSchema);
route to add user to chat:
// #route Put api/chats
// #desc Add a user to a chat
// #access Private
router.put('/', [auth,
[
check(
'code',
'Please include the code for the chat')
.not()
.isEmpty(),
check(
'password',
'Please include the password for the chat'
).not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const chat = await Chat.findOne({ code: req.body.code });
//const user = await User.findOne({ user: req.user.id });
if (!chat) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// Check if the chat has already been joined by the user
if (chat.users.filter(member => member.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Chat already joined' });
}
//console.log(chat.password);
const isMatch = await bcrypt.compare(req.body.password, chat.password);
if (!isMatch) {
return res.status(400).json({ errors: [{ msg: 'Invalid Credentials' }] });
}
const newUser = {
user: req.user.id,
text: req.user.name
}
chat.users.unshift(newUser);
await chat.save();
res.json(chat.users);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
User model:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('user', UserSchema);
in this part, it seems you are assigning the user's name to a text property, which I think it should be name not text.
const newUser = {
user: req.user.id,
text: req.user.name
}
The code should be:
const newUser = {
user: req.user.id,
name: req.user.name //Property should be name
}
I hope this works!
I am trying to create a method to userSchema however I get this error:
TypeError: Cannot set property 'generateAuthToken' of undefined
userSchema.methods.generateAuthToken = function() {
// const token = jwt.sign({ _id: user._id }, config.get("JWTPRIVATEKEY"));
const token = jwt.sign({ _id: this._id }, "Privatekey");
return token;
};
Note: I am not using arrow function instead its a normal function:
var userSchema = {
name: {
type: String,
require: true
},
email: {
type: String,
require: true,
unique: true
},
password: {
type: String
}
};
userSchema.methods.generateAuthToken = function() {
// const token = jwt.sign({ _id: user._id }, config.get("JWTPRIVATEKEY"));
const token = jwt.sign({ _id: this._id }, "Privatekey");
return token;
};
const User = mongoose.model("Users", new mongoose.Schema(userSchema));
function validateUser(user) {
let schema = {
name: Joi.string().required(),
email: Joi.string()
.required()
.email(),
password: Joi.string().required()
};
return Joi.validate(user, schema);
}
exports.User = User;
exports.validateUser = validateUser;
Use case
let user = await User.findOne({ email: req.body.email });
const token = user.generateAuthToken();
The problem is your userSchema just a normal object not a Schema Object. So it doesn't have methods property (your userSchema.methods is undefined) so when you set userSchema.methods.generateAuthToken, it will throw an Error.
Please take a look at the document
https://mongoosejs.com/docs/guide.html#methods
You should define Schema object first
var userSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
email: {
type: String,
require: true,
unique: true
},
password: {
type: String
}
});