How to use async and await with Express and MongoDB - express

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongodb = require('mongodb');
const app = express();
app.use(cors())
app.use(bodyParser.json());
const MongoClient = mongodb.MongoClient;
const url = "mongodb://localhost:27017/recart";
app.post("/register" , (req, res) => {
const { displayName, email, password } = req.body;
if (!email || !displayName || !password) {
return {code: 400, msg: "incorrect form submission"}
}
let insertedid = null
MongoClient.connect(url, (err, db) => {
if (err) throw err;
var dbo = db.db("recart");
var myobj = { displayName: displayName, email: email, password: password };
dbo.collection("customers").insertOne(myobj)
.then(res => {
insertedid = res.insertedId
})
});
res.json({id: insertedid})
})
app.listen(process.env.PORT || 3000, ()=> {
console.log(`App is running on port 3000`);
})
Here, I am storing data inside db and sending the last inserted id
to the frontend.
However, id is returning null.
How can I use async and await to solve this issue?

You can modify your post function using async await like this:
app.post("/register", async (req, res) => {
const { displayName, email, password } = req.body;
if (!email || !displayName || !password) {
return {code: 400, msg: "incorrect form submission"}
}
let insertedid = null ;
MongoClient.connect(url,{ useNewUrlParser: true }, async (err, db) => {
if (err) throw err;
var dbo = db.db("recart");
var myobj = { displayName: displayName, email: email, password: password };
var result = await dbo.collection("customers").insertOne(myobj);
insertedid = result.insertedId;
res.json({ id: insertedid})
});
})

Related

Getting error in MERN stack Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

I am new to MERN. I was using this code below . I followed a youtube tutorial and it was working fine for me for 4 to 5 days but now suddenly it has stopped working. I didn't change anything. I am not able to login, logout or even fetch data. My postman is giving positive results using these api but it won't work on my code. I want to remind you guys again, it was working fine for 4 to 5 days.
const User = require("../model/user");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const signup = async (req, res, next) => {
const { fname, lname, email, password, role, phone } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
console.log(err);
}
if (existingUser) {
return res.status(400).json({ message: "user already exists" });
}
const hashedPassword = bcrypt.hashSync(password);
const user = new User({
fname,
lname,
email,
password: hashedPassword,
phone,
role,
});
try {
await user.save();
} catch (err) {
console.log(err);
}
return res.status(201).json({ message: user });
};
const login = async (req, res, next) => {
const { email, password } = req.body;
let existingUser;
try {
existingUser = await User.findOne({ email: email });
} catch (err) {
console.log(err);
}
if (!existingUser) {
return res
.status(400)
.json({ message: "user doesn't exist. Please signup" });
}
const isPasswordCorrect = bcrypt.compareSync(password, existingUser.password);
if (!isPasswordCorrect) {
return res.status(401).json({ message: "invalid email or password" });
}
const token = jwt.sign({ id: existingUser._id }, "change1122", {
expiresIn: "1h",
});
res.cookie(String(existingUser._id), token, {
path: "/",
expires: new Date(Date.now() + 1000 * 3600),
httpOnly: true,
sameSite: "lax",
});
return res
.status(200)
.json({ message: "user logged in sucessfully", user: existingUser, token });
};
const verifyToken = (req, res, next) => {
const cookies = req.headers.cookie;
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
jwt.verify(String(token), "change1122", (err, user) => {
if (err) {
return res.status(404).json({ message: "invalid token" });
}
req.id = user.id;
});
next();
};
const getUser = async (req, res, next) => {
const id = req.id;
let user;
try {
user = await User.findById(id, "-password");
} catch (err) {
console.log(err);
}
if (!user) {
res.status(404).json({ message: "user not found with the id" });
}
return res.status(200).json({ user });
};
const logout = async (req, res, next) => {
const cookies = req.headers.cookie;
console.log(cookies);
const token = cookies.split("=")[1];
if (!token) {
res.status(404).json({ message: "no token found" });
}
const user = req.id;
res.clearCookie(`${user}`);
req.cookies[`${user}`] = "";
return res.status(200).json({ message: "successfully logged out" });
};
exports.signup = signup;
exports.login = login;
exports.verifyToken = verifyToken;
exports.getUser = getUser;
exports.logout = logout;
Here is the error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:372:5) at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (E:\Reacct\pos\server\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (E:\Reacct\pos\server\node_modules\express\lib\response.js:174:12)
at ServerResponse.json (E:\Reacct\pos\server\node_modules\express\lib\response.js:278:15)
at getUser (E:\Reacct\pos\server\controller\user-controller.js:86:25)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
code: 'ERR_HTTP_HEADERS_SENT'
}
[nodemon] app crashed - waiting for file changes before starting...
I think i have an issue with cookies or token, I am new so i don't understand it properly.

Router export failing while exporting multiple functions (TypeError: app.use() requires a middleware function)

I'm having this odd error and I'm not knowing what to do to make it work. The thing is, I need to export some functions and express router. The thing is, if I try to set
module.exports = {router, function1, function2}
it gaves me that error
(TypeError: app.use() requires a middleware function).
If I try to set my functions with exports.function1 = async function function1 (req,res) {blablabla} they get exported but I still get the same error... I need to use the functions in this way
router.get('/api/auth0/users', async (req, res,next) => {
function1(res, next)
})
and I'm lacking ideas... and have no clue of why the multiple module.exports it's not working since I've used it a lot (seems like the problem is with the router.... (NOTE: I've just used an example code since mine is a 140 lines src)
(NOTE2: function1 and function2 are async since they make queries to MongoDB)
UPDATE: (Adding the import codes)
I import it in my main .js file like this
const {router} = require('./auth/auth0')
then tell app to use it like this
app.use(router);
app is defined using this lines
const express = require("express");
const app = express();
changing the export/import name to another like authRouter or something makes no difference.
Heres the complete code:
const router = require('express').Router()
const express = require('express')
const passport = require('passport');
const session = require('express-session')
const {generateJwt} = require("../helpers/generateJwt");
const usuarios = require('../models/usuarios')
let OpenIDConnectStrategy = require('passport-openidconnect');
passport.serializeUser(function (user, cb) {
cb(null, user);
});
passport.deserializeUser(function (obj, cb) {
cb(null, obj);
});
passport.use(new OpenIDConnectStrategy({
issuer: 'https://' + process.env.AUTH0_DOMAIN + '/',
authorizationURL: 'https://' + process.env.AUTH0_DOMAIN + '/authorize',
tokenURL: 'https://' + process.env.AUTH0_DOMAIN + '/oauth/token',
userInfoURL: 'https://' + process.env.AUTH0_DOMAIN + '/userinfo',
clientID: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
callbackURL: '/login/callback',
scope: [ 'profile', 'email' ]
},
function verify(issuer, profile, cb) {
if(profile){
userEmail = profile.emails[0].value
userProfile = profile
whoIs = profile.id
}
return cb(null,profile)
}
));
router.use(express.json())
router.use(session({ secret: 'keyboard cat~troubles', secured:true, key: 'sid', saveUninitialized: true, resave: false}));
router.use(passport.initialize())
router.use(passport.session())
var userProfile = ""
let userEmail = ""
let whoIs = ""
let token = ""
async function createUser(res,next) {
try {
token = await generateJwt(whoIs, process.env.JWT_SECRET_KEY);
const nAccount = new usuarios({
nombre: userProfile.name.givenName,
apellido: userProfile.name.familyName,
auth0Id: whoIs,
email: userEmail,
token: token
});
await nAccount.save()
return res.status(201).json({Status: "Cuenta creada exitosamente", token: token});
} catch (error) {
console.log(error)
return res.redirect('/api/auth0/logged')
}
}
async function findUser(res, next){
try{
let email = userEmail
let mailEncontrado = await usuarios.findOne( {email} )
if (!mailEncontrado ){
return res.redirect('/api/auth0/register')
}
else {
token = await generateJwt(whoIs, process.env.JWT_SECRET_KEY);
let userID = await usuarios.findOneAndUpdate(
{email},
{ nombreAuth0: userProfile.name.givenName,
apellidoAuth0: userProfile.name.familyName,
auth0Id: whoIs,
token: token},
{ new: true }
)
return res.redirect('/api/auth0/logged')
}
}
catch (err) {
console.log(err)
}
}
async function userAuthenticated(res, next) {
if( req.isAuthenticated() === true){
console.log(req.isAuthenticated())
return true
} else{
console.log(req.isAuthenticated())
return false
}
}
router.get('/api/auth0/login', passport.authenticate('openidconnect',{prompt: 'login', failureMessage: true}));
router.get('/api/auth0/users', async (req, res,next) => {
findUser(res, next)
})
router.get('/api/auth0/register', async (req, res,next) => {
createUser(res, next)
})
router.get('/login/callback', passport.authenticate('openidconnect', {
successRedirect: '/api/auth0/users',
failureRedirect: '/api/auth0/login'
}));
router.get('/api/auth0/logged', (req, res) => {
if(whoIs === ""){
return res.status(401).json('Error de autenticacion')
}
else {
console.log(whoIs)
return res.status(201).json({Status: 'Usuario logueado. ID = '+ whoIs, Token: token, Email: userEmail})
}
})
router.get('/api/auth0/logout', (req, res) => {
if(!req.user){
res.json("No hay usuario autenticado")
}
req.logout()
res.status(201).json("Sesion finalizada exitosamente.")
})
module.exports = {router, userAuthenticated}

Every Time I try to do a post request in postman to http://localhost:8080/api/users

//userRoutes.js
const express = require("express");
const { registerUser } = require("../controller/userController");
const router = express.Router();
router.post("/", registerUser);
module.exports = router;
//server.js
const express = require("express");
const dotenv = require("dotenv");
const app = express();
// database
const connectDb = require("./db/db");
require("colors");
// router
const userRoute = require("./routes/userRoutes");
const port = process.env.PORT || 8080;
dotenv.config();
connectDb();
app.use(express.json());
app.use("/api/users", userRoute);
app.listen(port, () => {
console.log("");
console.log(
` Running on Port ${port} in ${process.env.MODE} Mode `.yellow.inverse
);
console.log("");
});
//userController.js
const User = require("../models/userModel");
const asyncHandler = require("express-async-handler");
const registerUser = asyncHandler(async (req, res) => {
console.log(req.body);
const { firstName, lastName, email, password } = req.body;
const exists = User.findOne({ email });
if (exists) {
res.status(400);
throw new Error("User Already Exists!");
}
const user = await User.create({ firstName, lastName, email, password });
if (user) {
res.status(200).json({
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
isAdmin: user.isAdmin,
});
} else {
res.status(404);
throw new Error("User Not Created");
}
});
module.exports = { registerUser };
SyntaxError: Unexpected token } in JSON at position 118
at JSON.parse (<anonymous>)
`enter code here` at parse (D:\tutorial\shppingapp\node_modules\body-parser\lib\types\json.js:89:19)
at D:\tutorial\shppingapp\node_modules\body-parser\lib\read.js:121:18
at invokeCallback (D:\tutorial\shppingapp\node_modules\raw-body\index.js:224:16)
at done (D:\tutorial\shppingapp\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (D:\tutorial\shppingapp\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (node:events:406:35)
at endReadableNT (node:internal/streams/readable:1331:12)
at processTicksAndRejections (node:internal/process/task_queues:83:21)
I think you are getting this error because of not sending well req in postman you have to send req as your User Schema defined in the model and once send well formated in postman body>text>json...

401: unauthorized (it contains bearer token)

Login module is working and creating users with token 201 created
Receipts route had worked with the same code before but now response is 401
using Postman (Authorization & Bearer: xcvbnm) has been put
Here is my code
server.js
const express = require('express')
require('./db/mongoose')
const userRouter = require('./routes/user')
const receiptRouter = require('./routes/receipt')
const app = express()
const port = process.env.PORT || 4003
app.use(express.json())
app.use(userRouter)
app.use(receiptRouter)
app.listen(port, () => {
console.log('Server is up on port ' + port)
})
const Receipt = require('./models/receipt')
const User = require('./models/user')
Receipts Route
router.post('/receipts', auth, async (req, res) => {
const receipt = new Receipt({
...req.body,
owner: req.user._id
})
try {
await receipt.save()
res.status(201).send(receipt)
} catch (e) {
res.status(400).send(e)
}
})
Receipt Schema
const Receipt = mongoose.model('Receipt', {
description: {
type: String,
required: true,
trim: true
},
})
Auth module
const auth = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer', '')
const decoded = jwt.verify(token, 'Mango#123')
const user = await User.findOne({ _id: decoded._id, 'tokens.token': token })
if (!user) {
throw new Error()
}
req.token = token
req.user = user
next()
} catch (e) {
res.status(401).send({ error: 'Please authenticate.' })
}
}

ForbiddenError: invalid csrf token

I am trying to use csrf in add employee function. However, whenever I hit submit I alway get ForbiddenError: invalid csrf token. I am not sure the way I did csrf correctly.
On the other hand, I have a login and register form. It works fine. And I did the same steps for add employee.
I've been reading some other posts but I didn't understand them. Can anyone point me to the correct way? I am really appreciate.
const express = require('express');
const router = express.Router();
const {uploadProduct, uploadForEmployee, uploadForVendor} = require('../upload');
const validation = require('validator');
const bcrypt = require('bcryptjs');
const csrf = require('csurf');
const csrfProtection = csrf();
//Load User model
const User = require('../../models/User');
router.get('/viewEmployee', (req, res) => {
User.find({$or: [{role: 'Admin'}, {role: 'Employee'}] }, (err, doc) => {
res.status(200).render('./admin/viewEmployee', {
user: req.user,
doc: doc,
link: "/admin/employees/"
})
})
});
router.delete('/:email', (req, res) => {
User.deleteOne({email: req.params.email}, (err, user) => {
if(err) {
console.log('Error while deleting product');
} else {
console.log(req.params.email + ' deleted');
res.redirect('/admin/employees/viewEmployee');
}
});
});
router.use(csrfProtection);
router.get('/addEmployee', (req, res) => {
res.render("./admin/employeeForm", {csrfToken: req.csrfToken()});
});
router.post('/addEmployee', uploadForEmployee.single('image'), (req, res) => {
let {name, email, password, phone, address, role} = req.body;
let image = req.file;
let errors = [];
if(!name || !email || ! password || !phone || !address || !role) {
errors.push({msg: 'Please enter all fields.'});
}
if(!image) {
errors.push({msg: 'Please upload a photo of yourself.'});
}
//sanitize input
name = validation.escape(name);
email = validation.escape(email);
password = validation.escape(password);
phone = validation.escape(phone);
address = validation.escape(address);
//Validate email
if(!validation.isEmail(email)){
errors.push({msg: 'Bad email.'})
} else {
email = validation.normalizeEmail(email, [
'all_lowercase', 'gmail_remove_dots','gmail_remove_subaddress', 'gmail_convert_googlemaildotcom',
'outlookdotcom_remove_subaddress', 'yahoo_remove_subaddress', 'icloud_remove_subaddress'
])
}
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters.' });
}
if (errors.length > 0) {
res.render('./admin/employeeForm', {
errors,
name,
email,
password,
phone,
address,
role,
image,
csrfToken: req.csrfToken()
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists.' });
res.render('./admin/employeeForm', {
errors,
name,
email,
password,
phone,
address,
role,
image,
csrfToken: req.csrfToken()
});
} else {
const newUser = new User({
name: name,
email: email,
password: password,
phone: phone,
address: address,
role: role,
imageUrl: '/images/employees/' + image.filename
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in.'
);
res.redirect('/admin/employees/viewEmployee');
})
.catch(err => console.log(err));
});
});
}
});
}
});
module.exports = router;
This is my login and register functions. These ones worked with csrf just fine.
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const passport = require('passport');
const {ensureLoggedIn, ensureLoggedOut} = require('connect-ensure-login');
const validation = require('validator');
const csrf = require('csurf');
const csrfProtection = csrf();
// Load User model
const User = require('../models/User');
//Profile
router.get('/profile', ensureLoggedIn('/users/login'), (req, res, next) => {
res.render('profile')
});
// Logout
router.get('/logout', (req, res) => {
req.logout();
req.flash('success_msg', 'You are logged out.');
res.redirect('/users/login');
});
// Register
router.post('/register', (req, res) => {
let { name, email, password, password2 } = req.body;
let errors = [];
if (!name || !email || !password || !password2) {
errors.push({ msg: 'Please enter all fields' });
}
//sanitize input
name = validation.escape(name);
email = validation.escape(email);
password = validation.escape(password);
password2 = validation.escape(password2);
//Validate email
if(!validation.isEmail(email)){
errors.push({msg: 'Bad email.'})
} else {
email = validation.normalizeEmail(email, [
'all_lowercase', 'gmail_remove_dots','gmail_remove_subaddress', 'gmail_convert_googlemaildotcom',
'outlookdotcom_remove_subaddress', 'yahoo_remove_subaddress', 'icloud_remove_subaddress'
])
}
if (password != password2) {
errors.push({ msg: 'Invalid Password.' });
}
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters.' });
}
if (errors.length > 0) {
res.render('./home/register', {
errors,
name,
email,
password,
password2,
csrfToken: req.csrfToken()
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists.' });
res.render('./home/register', {
errors,
name,
email,
password,
password2,
csrfToken: req.csrfToken()
});
} else {
const newUser = new User({
name,
email,
password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in.'
);
res.redirect('/users/login');
})
.catch(err => console.log(err));
});
});
}
});
}
});
// Login
router.post('/login', (req, res, next) => {
passport.authenticate('local', {
successReturnToOrRedirect: '/',
failureRedirect: '/users/login',
failureFlash: true
})(req, res, next);
});
router.use(csrfProtection);
// Login Page
router.get('/login', ensureLoggedOut('/'), (req, res, next) => {
res.render('./home/login', {
csrfToken: req.csrfToken()
})
});
// Register Page
router.get('/register', ensureLoggedOut('/'), (req, res, next) => {
res.render('./home/register', {
csrfToken: req.csrfToken()
})
});
module.exports = router;