Axios get request resolving after, therefore my variable doesnt get assigned - node.js

On my user controller , i want to send a get request to receive a json response.
When the response comes i want to assign the value to a variable calles embed, however the rendering part:
res.render('user', {
user,
title: user.name,
embed: embed.html,
});
Happens before the axis function is finished... leaving me with an empty object.
What do i have to do in order to wait for the response... and then render the template?
Note that console log 2 happens before console log 1 in this code:
exports.getUserBySlug = async (req, res, next) => {
const user = await User.findOne({ slug: req.params.slug })
let embed = {}
if (!user) return next();
axios.get(`https://soundcloud.com/oembed?format=json&url=${user.musicLink}`)
.then(response => {
embed = response.data
console.log('1: ', embed)
})
.catch(error => {
console.log(error);
})
console.log('2: ', embed)
res.render('user', {
user,
title: user.name,
embed: embed.html,
});
};

Request is asynchronous. You need to add one await more
exports.getUserBySlug = async (req, res, next) => {
const user = await User.findOne({ slug: req.params.slug })
let embed = {}
if (!user) return next();
await axios.get(`https://soundcloud.com/oembed?format=json&url=${user.musicLink}`)
.then(response => {
embed = response.data
console.log('1: ', embed)
})
.catch(error => {
console.log(error);
})
console.log('2: ', embed)
res.render('user', {
user,
title: user.name,
embed: embed.html,
});
};
Also I would recommend NOT to mix async/await with .then calls.
exports.getUserBySlug = async (req, res, next) => {
const user = await User.findOne({ slug: req.params.slug })
let embed = {}
if (!user) return next();
try {
const response = await axios.get(`https://soundcloud.com/oembed?format=json&url=${user.musicLink}`)
embed = response.data
} catch (e) {
console.error(e)
}
res.render('user', {
user,
title: user.name,
embed: embed.html,
});
};

Related

Reactjs: post data to localhost always pending

I am working on ReactJs and NodeJS and I am creating a signup page. I post data to server but it is always pending.
Which part did I do wrong? It would be nice if someone can help.
Front end:
const handleSubmit = (event) => {
// prevent page refresh
event.preventDefault();
const newUserData = {
name: name,
email: email,
password: password,
};
axios
.post("/signup", newUserData)
.then((res) => {
console.log(res.data);
})
.catch((error) => {
console.log(error);
});
setEmail("");
setName("");
setPassword("")
console.log("form submitted ✅");
};
Backend:
router.post("/signup", (req, res) => {
const { name, email, password } = req.body;
if (!email || !password || !name) {
res.status(422).send({ error: "Please add all the fields" });
}
console.log(req.body);
User.findOne({ email: email })
.then((savedUser) => {
if (savedUser) {
res.status(422).send({ error: "Email already been used" });
}
bcrypt.hash(password, 12).then((hashedpassword) => {
const user = new User({
name,
email,
password: hashedpassword,
});
user
.save()
.then((user) => {
res.json({ message: "Sign Up Successfully" });
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
});
in package.json i set proxy as
"proxy": "http://localhost:5000",
I guess you are using MongoDB as well, in that case keep in your mind that the findOne is async, so you need to use await before. And for to save data you need to use the .create() method from MongoDB, e.g.
router.post("/signup", async (req, res) => {
const { name, email, password } = req.body;
if (!email || !password || !name) {
res.status(422).send({ error: "Please add all the fields" });
}
console.log(req.body);
await User.findOne({ email: email })
.then((savedUser) => {
if (savedUser) {
// you need to add return to stop the code
return res.status(422).send({ error: "Email already been used" });
}
// or you can add else because the code keep running
bcrypt.hash(password, 12).then((hashedpassword) => {
const user = await User.create({
name,
email,
password: hashedpassword,
});
user
.save()
.then((user) => {
res.json({ message: "Sign Up Successfully" });
})
.catch((err) => {
console.log(err);
});
});
})
.catch((err) => {
console.log(err);
});
});
I think it is better to use something like throw new Error('Email already been used') instead of return for your res.status(422).send({ error: "Email already been used" }); because if you have return the server doesn't give back an error, but a normal answer, but of course it is ok if you want that.
I want you to be sure that before you submit, the values name, email, password, are updated. Please try:
const handleSubmit = async (event) => {
// prevent page refresh
event.preventDefault();
console.log(`The value for the name: ${name}`);
console.log(`The value for the email: ${email}`);
console.log(`The value for the password: ${password}`);
try {
const response = await axios.post("http://localhost:5000/signup", {
name,
email,
password,
});
console.log(response.data);
setEmail("");
setName("");
setPassword("");
console.log("form submitted ✅");
} catch (error) {
console.log(error);
}
};

add basic auth to express rest api

so I have rest api where i store user data in mongoDB, I want to add basic auth to my api but I'm stuck, I want to check if user is authorised on some paths, for example on /update, if user is auth perfom request, if not send that user is not authorized
my code where I store user is db
const addUser = async (req, res) => {
const checknick = await User.find({ nickname: req.body.nickname }) //checks if user exists with nickname
if (checknick.length !== 0) {
return res.send({
message: 'user already exists, please use another nickname',
})
}
const secretInfo = await hash(req.body.password).catch((err) =>
res.send('password is required!')
)
const user = new User({
name: req.body.name,
surname: req.body.surname,
nickname: req.body.nickname,
password: secretInfo.password,
salt: secretInfo.salt,
})
user.save((err, result) => {
if (err) {
return res.send(err)
}
res.send('user added sucesessfully')
})
}
and where I verify user
const verify = async (req, res) => {
const user = await User.findOne({ nickname: req.body.nickname })
if (!user) {
return
}
const { password } = await hash(req.body.password, user.salt).catch((err) =>
res.send('password is required')
)
const verifiedUser = await User.findOne({
nickname: req.body.nickname,
password: password,
})
if (!verifiedUser) {
return false
}
return true
}
and finally login logic
const login = async (req, res) => {
const access = await verify(req, res)
// console.log(req.headers)
if (access) {
res.send('logged in')
console.log(req.headers)
return
}
return res.status(401).send('failed to login')
}
everything works but I want to use authorizatuon header to send user and password information
This is how to restrict a route add this middleware function before the
route you want to restrict like this:
app.post("/update", restrictTo("admin"));
Every user must have a role to authorize. here I am handling error with a global error handler but you can handle error another way:
exports.restrictTo = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role))
return next(
new AppError('You dont have permission to do this action', 403)
);
next();
};
};

Why mongo query is showing undefined in node js

I'm trying to check the data with findOne when im trying with the postman getting undefined in console.log , i checked with the same query in roboMongo and its showing the data
this is the result:-
Here is the code:-
exports.signIn = async( req, res ) => {
const {
userEmailPhone,
} = req.body;
await User.findOne ({ email : userEmailPhone}).then((err, user)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}
the postman response:-
Since you are already using async - await, I believe there is no need of using the .then() block.
Your code should be updated to use async and await as below:
exports.signIn = async( req, res ) => {
const { email } = req.body;
const user = await User.findOne ({ email : userEmailPhone})
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
}
If you still want to use the .then() block, I would recommend making the following changes in the code:
exports.signIn = async ( req, res ) => {
const {email} = req.body;
User.findOne ({ email : email}).then((user, err)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}
Since the promise callback for MongoDb queries has the following callback format:
.then( (res, err) => {
// do stuff
})
Reference : https://docs.mongodb.com/drivers/node/fundamentals/promises/
You are sending raw json data. First you should use app.use(bodyParser.json());. Only app.use(bodyParser()); is deprecated.
This should fix it assuming you have a json body-parser
exports.signIn = async( req, res ) => {
const {email} = req.body;
User.findOne ({ email : email}).then((err, user)=> {
console.log("user..", user)
if (user){
res.status(200).send({
message: "sucess"
});
}
})
}

access the info of the user that created the post

I am making a react-native mobile app and I am having trouble passing the users info that created the post to the home page in the post detail. I can pass the userID but for some reason when I add the rest of the info to the payload I can't create a post. Please help.
BACKEND
This is the requireAuth file that requires authentication before performing a tast. My code for the user is here as well at the bottom---
const mongoose = require("mongoose");
const User = mongoose.model("User");
module.exports = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).send({ error: "You must be logged in." });
}
const token = authorization.replace("Bearer ", "");
jwt.verify(token, "mySecretKey", async (err, payload) => {
if (err) {
return res.status(401).send({ error: "You must be logged in." });
}
const { userId, name, phone, email } = payload;
const user = await User.findById(userId);
req.user = user;
console.log(req.user);
next();
});
};
This is the POST route for the Item---
router.post("/items", requireAuth, async (req, res) => {
const { title, category, detail, condition, price } = req.body;
if (!title || !category || !detail || !condition || !price) {
return res.status(422).send({
error: "You must provide a title, category, detail, condition, and price"
});
}
try {
const item = new Item({
title,
category,
detail,
condition,
price,
userId: req.user._id
});
await item.save();
res.send(item);
} catch (err) {
res.status(422).send({ error: err.message });
}
});
FRONT-END
This is my createItem function in the itemContext file---
const createItem = dispatch => async ({
title,
category,
detail,
condition,
price
}) => {
try {
const response = await sellerApi.post("/items", {
title,
category,
detail,
condition,
price
});
//this is the other place the error might be happening i need this to save in the phone local storage
dispatch({ type: "create_item", payload: response.data });
navigate("Home");
} catch (err) {
console.log(err);
}
};
All I am trying to do it is when the post is being displayed so is the info of the post creator
For existing post in the database: If you are referencing your user in post model like this
const Post = mongoose.model('Post', {
// other fields
userId: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
}
})
then you can use populate to fetch user of that post.
const post= await Post.findById('5c2e505a3253e18a43e612e6')
await post.populate('userId').execPopulate()
console.log(post.userId)

Node.js UnhandledPromiseRejectionWarning: Error: Can't set headers after they are sent

I'm new to node.js and want to send out dataof User and Match in a single response by querying mongodb twice .
router.get('/preview/', checkAuth, (req, res)=> {
const errors = {};
const match = {}
User.findOne({_id: req.user.id})
.then(user => {
if (!user) {
return res.status(404).json({errors: 'Could not find a user' });
}
Match.findOne({ user: req.user.id }).then(m => {
console.log('match found!');
match = m;
}).catch(err=> res.status(404).json(err)); // <-error occures here
res.status(200).json({user, match});
})
.catch(err=> res.status(404).json(err));
});
But I get this error:
(node:8056) UnhandledPromiseRejectionWarning: Error: Can't set headers after they are sent.
How can I fix it?
Please have a look at the comments added in your code.
router.get('/preview/', checkAuth, (req, res)=> {
const errors = {};
const match = {}
User.findOne({_id: req.user.id})
.then(user => {
if (!user) {
return res.status(404).json({errors: 'Could not find a user' });
}
Match.findOne({ user: req.user.id }).then(m => {
console.log('match found!');
match = m;
}).catch(err=> res.status(404).json(err)); // <-error occures here because you sent the response if error occurs
res.status(200).json({user, match}); // this will be executed even if there is an error so it will again try to send the response
})
.catch(err=> res.status(404).json(err));
});
Improved code:
router.get('/preview/', checkAuth, (req, res) => {
const errors = {};
User.findOne({ _id: req.user.id })
.then((user) => {
if (!user) {
return res.status(404).json({ errors: 'Could not find a user' });
}
Match.findOne({ user: req.user.id })
.then((m) => {
console.log('match found!');
res.status(200).json({ user, m }); // send the success response when the match found
})
.catch((err) => res.status(404).json(err)); // send the error response when erro thrown
})
.catch((err) => res.status(404).json(err));
});

Resources