Hello I want to find posts which user has made ..
I do my request with JWT Token:
###
http://localhost:8080/forum/getByOwnerID
Authorization: Bearer {{token}}
This is my create function :
exports.create = async (req, res) => {
const { forumName, forumDescription } = req.body;
const token = req.token;
const forumExist = await Forum.findOne({ forumName: req.body.forumName });
if(forumExist){
res.status(400).send("Forum Exists already.");
}
try{
const owner = await User.findOne({userID:token._id});
if (!forumName || !forumDescription) {
res.status(400);
throw new Error("Please Fill all the feilds");
return;
}
else {
const newForum = new Forum({ forumName, forumDescription,user: owner.userID });
newForum.user = owner;
const createdNote = await newForum.save();
res.status(201).json(createdNote);
}
}catch(err){
res.status(400).send(err);
}
};
This is my function where I want to get the Posts which the user has made :
exports.getByToken = async (req, res, next) => {
const forum = await Forum.findById( {user: req.token._id} );
if (forum) {
res.json(forum);
} else {
res.status(404).json({ message: "Forum not found" });
}
res.json(forum);
}
And this is model which I have for Post:
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
published_on: {
type: String,
default: moment().format("LLL")
},
});
Everytime I do a request it has this error :
UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ user: 'admin' }" (type Object) at path "_id" for model "Forum"
my generate Token :
const generateToken = (_id, userID) => {
console.log('Signing token for ID ', _id,userID);
console.log('Secret key is ', process.env.JWT_KEY);
const token = jwt.sign({ _id,userID}, process.env.JWT_KEY, {
expiresIn: "30d",
});
console.log('Signed token: ', token);
return token;
};
As you are using findById, you should only send the id as argument function.
If you want to search with filter query, use find method
Related
I am developing a MERN app with axios and trying to populate a field(songList) in a model (User) that is referencing the Schema.Types.ObjectId of another schema (Song).
The _id populates when I create a new Song appropriately.
I can see the entire referenced field is populated in the terminal server side with a console.log but the I cannot get the field to populate on the client side.
My Model; I am trying to populate songList.
const { Schema, model } = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
songList: [
{
type: Schema.Types.ObjectId,
ref: 'Song'
}
],
});
userSchema.pre('save', async function (next) {
if (this.isNew || this.isModified('password')) {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
next();
});
userSchema.methods.isCorrectPassword = async function (password) {
return bcrypt.compare(password, this.password);
};
const User = model("User", userSchema);
module.exports = User;
My server side query, console.log(userSongs) and console.log(user.songList) shows the array of songs appropriately in the terminal:
//login
router.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
User.findOne({ username: username })
.populate({path: "songList"})
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
res.status(404).json({ message: 'User Not Found' });
}
const passwordIsValid = bcrypt.compareSync(
password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password" });
}
let userSongs = [];
for (let i = 0; i < user.songList.length; i++) {
userSongs.push(user.songList[i])
}
const accessToken = sign(
{ username: user.username, _id: user._id },
"importantsecret");
// res.json({
// token: accessToken,
// username: username,
// _id: user._id,
// songList: user.songList
// });
res.status(200).send({
token: accessToken,
username: username,
_id: user._id,
userSongs: userSongs
});
console.log(userSongs)
});
});
The client side request for the user information where I am hoping to see a populated songList with console.log(singleUser):
const [singleUser, setSingleUser] = useState({})
const [userSongs, setUserSongs] = useState([])
useEffect(() => {
Axios.get(`http://localhost:3001/api/user/${id}`).then((response) => {
setSingleUser(response.data)
})
}, [authState])
Client side login request.
const login = () => {
const data = { username: username, password: password };
Axios
.post("http://localhost:3001/api/user/login", data)
.then((response) => {
if (response.data.error) {
console.log(response.data.error)
} else {
localStorage.setItem('accessToken', response.data.token)
setAuthState({
username: response.data.username,
_id: response.data._id,
status: true
});
window.location.replace('/')
}
})
}
Here is where I create a new Song and add it to the user that is logged in.
router.post('/insert', (req, res) => {
const id = req.body.id;
const songTitle = req.body.songTitle;
const artist = req.body.artist;
const album = req.body.album;
Song.create({ songTitle: songTitle, artist: artist, album: album })
.then((song) => {
return User.findOneAndUpdate(
{ _id: id },
{ $addToSet: { songList: song._id } },
{ new: true }
)
})
.then((user) =>
!user
? res.status(404).json({
message: 'Song created, but found no user with that ID',
})
: res.json('Created the song')
)
.catch((err) => {
console.log(err);
res.status(500).json(err)
})
});
Any suggests on how I can get songList to populate using the populate() mongoose function is much appreciated.
Thank you,
Brandon
I've read articles on stack overflow, 'Mongoose 'populate' not populating",
"Mongoose .populate() not working correctly". Medium articles, and the mongoose documentation.
I've tried sending the user songs in the response back as res.json() and res.send(). The field shows up but is not populated.
I've tried being more specific with songList.songTitle and {path: "songList")
All of these show the field populated in the terminal but not on the front side.
How can I make a method in which if a user sign up that user get a 4 digit id? And that id increases whenever new user signup?
In NodeJS that id should be random but it should be at least 4 digit and it move on & on whenever a new user sign up but the minimum value should be 4 digit only so how can I built that method? if its a large process than please explain me I will get a idea or if its easy and it can be done in less lines of code than please provide snippets of that example you can check my code
user.controller:
const user_module = require('./user.modules');
class user_controller extends user_module {
static create_user = async (req, res) =>{
try {
console.log("controller response",req.body)
let response = await this.save_user_details(req)
let message = 'Success';
res.send({
sucess: true,
message: message,
data: response
})
} catch (error) {
let status_code = error.status.code != undefined ? error.status_code: 500;
let type = error.type != undefined ? err.type: 'Bad Request';
let message = err.custom_msg != undefined ? error.custom_msg: "Something went wrong"
res.status(status_code).send({
sucess: false,
error:type,
message: message
})
}
}
static get_users = async (req, res) =>{
try {
console.log("controller response",req.body)
let response = await this.retrieve_user(req)
let message = 'Success';
res.send({
sucess: true,
message: message,
data: response
})
} catch (error) {
let status_code = error.status.code != undefined ? error.status_code: 500;
let type = error.type != undefined ? err.type: 'Bad Request';
let message = err.custom_msg != undefined ? error.custom_msg: "Something went wrong"
res.status(status_code).send({
sucess: false,
error:type,
message: message
})
}
}
static otp_verify = async (req, res) =>{
try {
console.log("controller response",req.body)
let response = await this.verify_user(req)
if(response.status){
res.send({
success: true,
message: response.message,
data: response.user
})
}else{
res.status(400).send({
success: false,
error: false,
message: response.message
})
}
} catch (error) {
let status_code = error.status_code != undefined ? error.status_code: 500;
let type = error.type != undefined ? err.type: 'Bad Request';
let message = error.custom_msg != undefined ? error.custom_msg: "Something went wrong"
res.status(status_code).send({
sucess: false,
error:type,
message:message
})
res.end();
}
}
}
module.exports = user_controller
user.modules:
const models = require('../models');
class user_module {
static save_user_details = async (req) => {
try {
console.log("req body", req.body)
const { profileImage } = req.body
let set_data = req.body
if (!!profileImage) {
set_data.profileImage = profileImage
}
return await models.users.create(set_data)
} catch (error) {
throw error
}
}
static retrieve_user = async (req) => {
try {
let {limit, pagination} = req.query
let query = {}
let projection ={__v: 0}
let options = {
lean: true,
sort: {_id: -1},
skip: !Number(pagination) ? 0: Number(pagination) * !Number(limit) ? 10: Number(limit),
limit: !Number(limit) ? 10: Number(limit)
}
let users = await models.users.find(query, projection, options)
let count = await models.users.count(query)
return {users, count}
} catch (error) {
throw error
}
}
static verify_user = async (req) => {
try {
console.log("req body", req.body)
const { otp, user_id } = req.body
if(otp == '123456'){
let user = await models.users.findById(user_id)
return {user: user, status: true, message: 'success'}
}else{
return {user: null, status: false, message: 'Otp Invalid'}
}
} catch (error) {
throw error
}
}
}
module.exports = user_module
Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Users = new Schema({
name: String,
phoneNumber: String,
userid: String,
profileImage: {
type: String,
default: 'https://www.pngitem.com/pimgs/m/146-1468479_my-profile-icon-blank-profile-picture-circle-hd.png'
},
about: String,
selectedCountry: {
type: Object
}
})
module.exports = mongoose.model("users", Users)
books-controllers
I want the data to appear by the name in the postman and not the ID because I have information and I want to fetch it through the name in the database
const getByName = async (req, res, next) => {
const name = req.params.name;
let book;
try {
book = await Book.getByName("name");
} catch (err) {
console.log(err);
}
if (!book)
return res.status(404).json({ message: "No book found" });
}
return res.status(200).json({ book });
};
modelSchema
Here is the Skyma model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bookSchema = new Schema({
name: {
type: String,
require: true
},
description: {
type: String,
require: true
},
price: {
type: Number,
require: true
},
avilable: {
type: Boolean,
},
image: {
type: String,
require: true
},
});
module.exports = mongoose.model("Book", bookSchema);
There in no in-built method in mongoose getByName. You can use generic find to search for object using name
let book = await Book.find({ name: name }).exec();
You can also use findOne if needed.
You can try this -
async function getByName(req, res){
const bookname = req.params.name ;
try {
const book = await Book.findOne({name: bookname})
if(book!==null) {
res.status(200).send({'data': book}) ;
}
else {
res.status(404).send("No book found !")
}
}
catch(error) {
console.log(error)
res.send("Error")
}
}
I'm learning node.js and it's amazing, especially with mongo, but sometimes I struggle to solve a simple problem, like patching only 1 attribute in my user database.
It's easier to patch something that cannot be unique, but I want to patch an username attribute and I defined it as "unique" in my schema. I don't know why, but MongoDB doesn't care other db entry has the same user, it let me save.
My schema:
/** #format */
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema(
{
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true, unique: true },
userNumber: { type: Number, required: true },
description: { type: String },
verified: { type: Boolean, default: false },
isAdmin: { type: Boolean, default: false },
isSubscriber: { type: Boolean, default: false },
isDisabled: { type: Boolean, default: false },
acceptedTerms: { type: Number, required: true },
},
{ timestamps: true }
);
module.exports = mongoose.model('User', userSchema);
On my user controllers in node, I want to updateOne({ _id: userId}, { username: myNewUsername} but it always happens, it doesn't take into consideration another db entry can have the username, so I tried a different strategy but it doesn't work:
exports.changeUsername = (req, res, next) => {
// Requirements
const userId = req.params.userId;
const newUsername = req.body.username;
console.log('userId: ' + userId);
console.log('newUsername: ' + req.body.username);
User.findOne({ username: req.body.username })
.then(result => {
console.log(result);
if (result.username) {
const error = new Error('Could not find this sport');
error.code = 'DUPLICATED';
throw error;
}
return;
})
.catch(err => next(err));
// if no username was in use then updateOne
User.updateOne({ _id: userId }, { username: newUsername })
.then(result => {
res.status(200).json({
message: 'username has been updated',
username: result.username,
});
})
.catch(err => next(err));
};
I don't know if I can updateOne at the same time add some find validation. What I am doing wrong? Users cannot have the same username.
On the console, it seems it works, but it throws an extra error I don't understand:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (/Users/username/Sites/pipi-api/node_modules/express/lib/response.js:776:10)
I tried this other approach and it works, but doesn't trigger an error if the record is not unique as I stated in the schema.
// GET ONLY ONE SPORT BY ID
exports.changeUsername = async (req, res, next) => {
// Requirements
const userId = req.params.userId;
const newUsername = req.body.username;
console.log('userId: ' + userId);
console.log('newUsername: ' + req.body.username);
try {
const oldUsername = await User.findOne({ username: newUsername });
if (oldUsername.username) {
throw new Error('Error: its duplicated');
}
const user = await User.findOneAndUpdate(
{ _id: userId },
{ username: newUsername },
{ new: true }
);
console.log('User successfully updated.');
return res.status(200).json({ success: true, user });
} catch (err) {
console.log('ERROR: ', err);
return res.status(400).json({ success: false });
}
};
If I uncomment the code above, it triggers an error if I find a record on the database that matches but it doesn't allow me to continue to my next line of codes I the username is not found on the db.
I get a new error:
userId: 6231bdef334afbde85ed9f43
newUsername: tetete
ERROR: TypeError: Cannot read properties of null (reading 'username')
at exports.changeUsername (/Users/user/Sites/pipi-api/v1/controllers/users/index.js:43:21)
That error is not related to Mongo. It means that you are trying to send a response and the response is already sent.
The issue is because you called both User.findOne and User.updateOne and both of them has .then handler. So the first one of these that finishes will send the actual response. In the moment the second one finished, the response is already send and the error is thrown because you are trying to send response again.
Mongo will throw the error if you try to change username property that some other user already have. You should check if the req.params.userId and req.body.username sent correctly to the backend. Try to console.log() them and check if they are maybe null.
Consider refactoring your handler to use async/await instead of then/catch. You can do it like this:
exports.changeUsername = async (req, res, next) => {
try {
const userId = req.params.userId;
const newUsername = req.body.username;
const user = await User.findOneAndUpdate({ _id: userId }, { username: newUsername }, { new: true });
console.log('User successfully updated.');
return res.status(200).json({ success: true, user });
} catch (error) {
console.log('ERROR: ', error);
return res.status(400).json({ success: false });
}
}
I've been struggling with this issue for a day now and can't seem to figure out a way to resolve it. This is the code I'm running
Client side:
const nameInput = document.querySelector("#nameInput");
const urlInput = document.querySelector("#urlInput");
const rowAlert = document.querySelector(".alertAppend");
const divAlert = document.createElement("div");
const nameUpdate = async (e) => {
e.preventDefault();
fetch("/auth/updateName", {
method: 'POST',
headers: {
'Content-Type' : 'application/json'
},
body: JSON.stringify({
name: nameInput,
url: urlInput,
})
})
.then(function (data) {
console.log('Request success: ', data);
})
.catch(function (error) {
console.log('Request failure: ', error);
});
};
submitName.addEventListener("click", nameUpdate);
API:
router.get("/updateName", auth, async (req, res) =>{
try {
const { name, url } = req.body;
const ime = name;
const uid = req.session.passport.user;
db.User.find({ where: { id: uid } })
.on('success', function (user) {
if (user) {
user.update({
name: ime,
webhook: url
})
.success(function () {})
}
})
res.json({ message: url});
} catch (err) {
if (err) res.status(500).json({ message: "Internal Error"})
}
});
For some reason it just runs the select query and never proceeds to update the user.
Chrome console output
Debug console output
Sequelize model in case it helps:
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
validate: {
isEmail: true
}
},
password: {
type: DataTypes.STRING,
allowNull: false
},
name: {
type: DataTypes.STRING
}
})
return User;
}
The issue was in the API, it's supposed to be router.post
router.post("/updateName", auth, async (req, res) =>{
const { ime, url } = req.body;
const uid = req.session.passport.user;
console.log(ime);
db.User.findOne({where: {id: uid}})
.then(record => {
let values = {
name: ime,
webhook: url
}
record.update(values).then( updatedRecord => {
console.log(`updated record ${JSON.stringify(updatedRecord,null,2)}`)
res.status(200).json({ message: "success"});
})
}
})
.catch((error) => {
// do seomthing with the error
throw new Error(error)
})
});
You can try the following code
await db.User.update({
name: ime,
webhook: url
}, { where: { id: uid } });
When defining your model I don't see the webhook field