res.status is not a function - express - node.js

I have a route to get user details. Here is the controller :
module.exports.getUser = (req, res) => {
if (req.method == "GET") {
const userDetails = `SELECT * FROM users WHERE id = ${req.params.id}`;
sql.query(userDetails, function (err, res) {
if (res.length > 0) {
res.status(200).json({ res })
} else {
res.status(401).json({ message: "Error with this id !" })
}
})
}
}
When i make a request to the url, I have this error :
TypeError: res.status is not a function
at Query.<anonymous> (/Applications/MAMP/htdocs/my-app/controllers/auth.controller.js:9:21)
Line 9 is res.status(200).json({ res })
Is there an error in this method ?
Thank you

If I understand your issue correctly, as per your comments. You have to change the variable you are using in sql query callback not the one you are receiving in getUser function
module.exports.getUser = (req, res) => {
if (req.method == "GET") {
const userDetails = `SELECT * FROM users WHERE id = ${req.params.id}`;
sql.query(userDetails, function (err, user) {
if (user.length > 0) {
res.status(200).json({ user })
} else {
res.status(401).json({ message: "Error with this id !" })
}
})
}
}
Something like this should work.

Related

req.params returns undefiened

I'm trying to get back the ID from the params but it keeps sending back undefiened, what would be the problem here and how can i solve it ?
this is the route:
app.delete(`${api_version}/delete-branch/:id`, verifyToken, branches.deleteBranch)
this is the controller:
exports.deleteBranch = (req, result) => {
const {branch_id} = req.params
console.log(branch_id) // => returns undefined
if(branch_id === undefined) {
result.status(404).send({
message: 'This branch does not exist',
statusCode: 404
})
} else {
// console.log(req.params)
Branches.deleteBranch(branch_id, (err, data) => {
if (err) {
result.status(500).send({
message: err.message
})
} else {
result.status(200).send({
message: 'Branch deleted successfully',
statusCode: 200,
data
})
}
})
}
}
You need to destruct req.params like this:
const {id} = req.params
instead of:
const {branch_id} = req.params
Or either defined the route as follow:
app.delete(`${api_version}/delete-branch/:branch_id`, verifyToken, branches.deleteBranch)
and then destruct const {branch_id} = req.params;

convert simple callbacks into async await

I am finding it hard to convert this user controllers code to async await. Can someone please help and guide me how can i do it too. So that i can also change any callbacks into async await.
Also if someone can provide a good source so that i can read about async await and how to apply them properly.
const User = require("../models/user")
exports.getUserById = (req, res, next, id) => {
User.findById(id).exec((error, user) => {
if (error || !user) {
return res.status(400).json({
error: "No user was found in DB"
})
}
req.profile = user
next()
})
}
exports.getUser = (req, res) => {
req.profile.salt = undefined;
req.profile.encrypted_password = undefined;
return res.json(req.profile)
}
exports.getAllUsers = (req, res) => {
User.find().exec((error, users) => {
if (error || !users) {
return res.status(400).json({
error: "No users was found in DB"
})
}
return res.json(users)
})
}
exports.updateUser = (req, res) => {
User.findByIdAndUpdate(
{ _id: req.profile._id },
{ $set: req.body },
{ new: true, useFindAndModify: false },
(error, user) => {
if (error) {
return res.status(400).json({
error: "You are not authorized to update this info"
})
}
user.salt = undefined;
user.encrypted_password = undefined;
res.json(user)
}
)
}
It should look something like this:
const User = require("../models/user");
exports.getUserById = async (req, res, next, id) => {
let user = await User.findById(id);
try {
if (!user) {
return res.status(404).json({
error: "No user was found in DB"
});
}
req.profile = user;
next();
} catch (err) {
return res.status(500).json({
error: "Something went wrong"
});
}
};
exports.getUser = (req, res) => {
req.profile.salt = undefined;
req.profile.encrypted_password = undefined;
return res.json(req.profile);
};
exports.getAllUsers = async (req, res) => {
let users = await User.find();
try {
if (users.length < 1) {
return res.status(404).json({
error: "No users was found in DB"
});
}
return res.json(users);
} catch (err) {
return res.status(500).json({
error: "Something went wrong"
});
}
};
exports.updateUser = async (req, res) => {
try {
let user = await User.findByIdAndUpdate(
{ _id: req.profile._id },
{ $set: req.body },
{ new: true, useFindAndModify: false }
);
user.salt = undefined;
user.encrypted_password = undefined;
return res.json(user);
} catch (err) {
return res.status(400).json({
error: "You are not authorized to update this info"
});
}
};
You should send back 404 errors if you cant find any user in the database. 400 means bad request.
You can achieve what you are asking by wrapping the function with Promise. In your example, you should use the solution given by Ifaruki, because mongoose already supports promises.
function waitSeconds(seconds) {
return new Promise(res => {
setTimeout(() => {
res();
}, seconds * 1000)
})
}
async function foo() {
console.log("Hello");
await waitSeconds(5);
console.log("World");
}
Here you can learn more about async in javascript

Error: Can't set headers after they are sent. NodeJS used async function

I faced to a little problem which blocks me. I'm working on authentication user service for my app used Node.js. I'm working on a PUT user route and need to compare the old and new password used bcrypt.
Sense adding a comparative try/catch I'm getting the following error:
Error: Can't set headers after they are sent.
app.put(`/users/:email`, checkAuthenticated, envisionDuplicateEmails, async (req, res) => {
const accountEmail = req.params.email
body = req.body
const user = users.find((user) => user.email === accountEmail)
const index = users.indexOf(user)
if (!user) {
res.status(500).send('Account not found.');
} else {
try {
if (await bcrypt.compare(body.password, user.password)) {
body.password = user.password
} else {
const hashedPassword = await bcrypt.hash(body.password, 10)
body.password = hashedPassword
}
} catch (e) {
return res.status(500).send('Internal server error');
}
const updatedAccount = { ...user, ...body }
users[index] = updatedAccount
res.redirect('/')
}
})
utility functions:
function checkAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login')
}
function envisionDuplicateEmails(req, res, next) {
accountEmail = req.params.email
bodyEmail = req.body.email
if (bodyEmail) {
if (bodyEmail != accountEmail) {
checkEmailExist(req, res, next)
}
}
return next()
}
function checkEmailExist(req, res, next) {
const accountEmail = req.body.email
const getAccount = users.find((user) => user.email === accountEmail)
if (getAccount === undefined) {
} else {
return res.status(500).send({ 'message': 'Account email already exist' })
}
return next()
}
Thanks for help :P
You are trying to re-execute the res.status(500) twice.
In your try/catch clause, just add the return keyword like that:
try {
if (await bcrypt.compare(body.password, user.password)) {
body.password = user.password
} else {
const hashedPassword = await bcrypt.hash(body.password, 10)
body.password = hashedPassword
}
} catch (e) {
// I've added the return keyword here
return res.status(500).send('Internal server error');
}
Now, when your try/catch catch an error, the code not continue and stop here.

I'm getting a weird error while using "next-connect" in my nextJS project that had fixed itself in dev but is now even worse in prod

As i said in the title i am using a npm package called "next-connect" to structure my api. Every api route that i created suffered from this error. This is the error :
Unhandled rejection: TypeError: Cannot read property 'end' of undefined
at next (/var/task/node_modules/next-connect/lib/index.js:43:54)
at next (/var/task/node_modules/next-connect/lib/index.js:49:9)
at next (/var/task/node_modules/next-connect/lib/index.js:58:16)
at next (/var/task/node_modules/next-connect/lib/index.js:49:9)
at next (/var/task/node_modules/next-connect/lib/index.js:58:16)
at next (/var/task/node_modules/next-connect/lib/index.js:58:16)
at next (/var/task/node_modules/next-connect/lib/index.js:60:9)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:228:7)
After a few minutes of trying i get to solve it in dev. Most of this due to messing with the .env file.
Here the code from my API Route :
import nextConnect from "next-connect";
import bcrypt from "bcryptjs";
import middleware from "../../middlewares/middleware";
const handler = nextConnect();
handler.use(middleware);
handler.get((req, res) => {
if (req.user) {
const { name, email, bio, profilePicture, emailVerified } = req.user;
return res.status(200).send({
status: "ok",
data: {
isLoggedIn: true,
user: {
name,
email,
bio,
profilePicture,
emailVerified
}
}
});
}
return res.status(200).send({
status: "ok",
data: {
isLoggedIn: false,
user: {}
}
});
});
handler.post((req, res) => {
const { email, password } = req.body;
return req.db
.collection("users")
.findOne({ email })
.then(user => {
if (user) {
return bcrypt.compare(password, user.password).then(result => {
if (result) return Promise.resolve(user);
return Promise.reject(Error("The password you entered is incorrect"));
});
}
return Promise.reject(Error("The email does not exist"));
})
.then(user => {
req.session.userId = user._id;
return res.send({
status: "ok",
message: `Welcome back, ${user.name}!`
});
})
.catch(error =>
res.send({
status: "error",
message: error.toString()
})
);
});
handler.delete((req, res) => {
delete req.session.userId;
return res.status(200).send({
status: "ok",
message: "You have been logged out."
});
});
export default handler;
And here code from the next-connect package (the one mentioned in the error report) :
module.exports = () => {
function connect(req, res) {
connect.handle(req, res);
}
connect.stack = [];
function add(method, ...handle) {
for (let i = 0; i < handle.length; i += 1) {
if (handle[i].stack) Object.assign(this.stack, handle[i].stack);
else this.stack.push({ handle: handle[i], method });
}
}
// method routing
connect.get = add.bind(connect, 'GET');
connect.head = add.bind(connect, 'HEAD');
connect.post = add.bind(connect, 'POST');
connect.put = add.bind(connect, 'PUT');
connect.delete = add.bind(connect, 'DELETE');
connect.options = add.bind(connect, 'OPTIONS');
connect.trace = add.bind(connect, 'TRACE');
connect.patch = add.bind(connect, 'PATCH');
// middleware
connect.use = add.bind(connect, '');
connect.error = add.bind(connect, 'ERR');
connect.apply = function apply(req, res) {
return new Promise((resolve) => this.handle(req, res, resolve));
};
connect.handle = function handle(req, res, done) {
let idx = 0;
const { stack } = this;
async function next(err) {
const layer = stack[idx];
idx += 1;
// all done
if (!layer) {
if (done) done();
else if (!res.headersSent) res.writeHead(404).end();
return;
}
// check if is correct method or middleware
if (layer.method !== '' && layer.method !== 'ERR' && layer.method !== req.method) {
next(err);
return;
}
try {
if (!err) { await layer.handle(req, res, next); return; }
// there is an error
if (layer.method === 'ERR' || layer.handle.length === 4) {
await layer.handle(err, req, res, next);
} else next(err);
} catch (error) {
next(error);
}
}
// Init stack chain
next();
};
return connect;
};

User-role based middleware return appropriate HTTP response status code

I am creating a middleware for admin users:
let admin_middleware = (req, res, next) => {
let token = req.header('x-auth');
User.findByToken(token).then((user) => {
if(!user || user._doc.user_type !== user_roles.admin)
return Promise.reject();
req.user = user;
req.token = token;
next();
}).catch((e) => {
res.status(401).send();
})
};
The question is around this line of code:
if(!user || user._doc.user_type !== user_roles.admin)
return Promise.reject();
When using the admin_middleware in server.js I catch the rejected promise :
.catch(e => {
res.status(401).send();
})
which means that for both: user not authorized and `access forbidden I return the 401 status code.
I would like to return 403 in one case, and 401 in another. What would be the solution?
Thank you!
As suggested in comments. You can reject with custom error like this:
class UnauthorizedError extends Error {}
class ForbiddenError extends Error {}
let admin_middleware = (req, res, next) => {
let token = req.header('x-auth');
User.findByToken(token).then((user) => {
if (!user) return Promise.reject(new UnauthorizedError());
if (user._doc.user_type !== user_roles.admin) return Promise.reject(new ForbiddenError());
req.user = user;
req.token = token;
next();
}).catch((e) => {
if (e instanceof UnauthorizedError) {
res.status(401);
} else if (e instanceof ForbiddenError) {
res.status(403);
} else {
res.status(500);
}
res.send();
})
};

Resources