I'm trying to check if a user is already existing in my DB. For now, I have a very simple controller in express :
app.post('/signup', jsonParser, (req, res) => {
User.findOne(
{ email: req.body.credentials.email },
function (_err, _data) {
res.status(409).send('hello');
}
);
})
If a user indeed exists with the email from the request, a 409 status is sent. But if no user exist matching the email, no response is ever sent. Is this normal behavior ? If yes, how do I callback a function for when I have no result on the findOne ?
Using Mongoose 4.7.7
Problem solved, a schema.post('findOne', function) was defined for the User model and next() was not always called.
Related
I have a Nodejs typescript authentication system that works using passport.
My problem is that when I use req.user in a route I get this error on req.user: Object is possibly 'undefined'.
This is a normal behavior of Typescript but I am using a middleware to protect routes that I want to use req.user in them and this way req.user cannot be undefined.
This is where I extend Express.User Type:
import { users } from "#prisma/client";
declare global {
namespace Express {
interface User extends users {}
}
}
This is the middleware that I'm using to protect routes for logged in users:
export function checkIsAuthenticated(req: Request, res: Response, next: NextFunction) {
if (req.isAuthenticated()) {
if (!req.user) req.logOut();
else return next();
}
res.status(400).json({
errors: [{ message: "no user logged in" }],
});
}
And this is the route for getting the user info:
export function userRoute(req: Request, res: Response) { // defining the route
res.json({
id: req.user.id, // I'm getting the error in these 4 lines
username: req.user.username, //
email: req.user.email, //
role: req.user.role, //
});
}
router.get("/user", checkIsAuthenticated, userRoute); // using the route
I don't want to check if user is defined because I don't want to do it in every route and that's not a good practice. That's why middleware are for.
I'm not good at Typescript so I need some help to fix it. Thanks
I don't want to check if user is defined because I don't want to do it in every route and that's not a good practice.
My first solution trying to accommodate that requirement didn't work, so I've removed it. I've left my second solution, which is:
But, I'd quibble that there's nothing wrong with checking the request for a user and giving yourself a nice useful error message if you've accidentally used one of these handlers on a route that you forgot to put the authentication on. That would look like this:
type RequestWithUser = Request & {user: typeOfUserObject};
function assertHasUser(req: Request): asserts req is RequestWithUser {
if (!( "user" in req)) {
throw new Error("Request object without user found unexpectedly");
}
}
Then your handler for those routes:
export function userRoute(req: Request, res: Response) {
assertHasUser(req);
// ...you can use `req.user` here...
});
Playground example
(You don't really need the explicit RequestWithUser type, you could just use asserts req is Request & {user: typeOfUserObject}.)
export function userRoute(req: Request, res: Response) { // defining the route
res.json({
id: req.user!.id, // you tell typescript that req.user for sure not. null
username: req.user!.username, //
email: req.user!.email, //
role: req.user!.role, //
});
}
router.get("/user", checkIsAuthenticated, userRoute);
I have used one controller and one model. After post form , i am checking either email id already exist or not. For checking email using express custom validator in controller. Inside controller call model function in which check email using mongodb. But every time condition false. Please guide me , i am new in node js.
enter image description here
Looks like the express-validator already show this as a use case of custom validators:
https://express-validator.github.io/docs/custom-validators-sanitizers.html
const { body } = require('express-validator/check');
app.post('/user', body('email').custom(value => {
return User.findUserByEmail(value).then(user => {
if (user) {
return Promise.reject('E-mail already in use');
}
});
}), (req, res) => {
// Handle the request
});
I am setting up Apollo Server on my Node app and wondered about moving the functionality over to Apollo.
I have business logic like this:
router.post(
'/login',
(req, res, next) => {
if (!req.body.email || !req.body.password) {
return 'You must send the username and the password.';
}
Users.findOne({ email: req.body.email })
.then(user => {
bcrypt.compare(req.body.password, user.password, (err, success) => {
req.user = user;
next();
});
})
},
auth.createToken,
auth.createRefreshToken,
auth.logUserActivity,
(req, res) => {
res.status(201).send({
success: true,
authToken: req.authToken,
refreshToken: req.refreshToken
});
}
);
It follows Node router architecture where I add the found user object to req object, which passes the user to the next functions - createToken etc.. using the next() function. This was ok for my server before trying to introduce GraphQL/Apollo, but now I want all this logic to be easily accessible to the Apollo resolvers.
I often hear that people are having an easy time turning their server from REST/non-GraphQL into a GraphQL server, but at the moment it's looking like it's going to be a bit of a job to go through all the logic and separate everything in to their own functions which take parameters directly rather than using the req object.
Is this a correct assumption? Or am I missing something?
Thanks!
Migrating the code you have shown above would be a very easy task. Once you build your graphql server and create your schema, etc. Then all you need to do is create login mutation. Then your resolver would handle the logic you have shown above. Then, instead of pulling the values from from req.body they would be function parameters.
A good pattern I am currently following is creating a login method on the model itself. Then the resolver calls the method on the schema (Here is an example of a project I'm doing it on now: Login method. Then here is an example of what the resolver looks like: Resolver
Hopefully that helped!
I want to have the functionality that user can change their password.
I've implemented a route ('/resetPasswd') like this:
UserRouter.route('/resetPasswd')
.post(function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
user.changePassword(req.body.oldPassword, req.body.newPassword, function (err, user) {
if (err) next(err);
res.json('password changes successfully !');
})
})(req, res, next);
});
this is what I send as the body:
{
"oldpassword": "secret",
"newPassword": "new"
}
But I get this error as response:
{
"message": "user.changePassword is not a function",
"error": {}
}
and this is a picture of my schema:
user schema:
I don't think I should declare the changePassword function in my schema (since it is provided by the passport-local-mongoose, although I added it but still get the same error) What mistake am I making here?
Someone had the same issue last night actually. Their problem was the package needed to be updated. I would check that you're on the latest version.
Here is what i did in my controller handling the reset password,
exports.editPassword = async (req, res) => {
const user = await User.findOne({
username: req.user.username
});
await user.setPassword(req.body.password);
const updatedUser = await user.save();
req.login(updatedUser);
req.flash('success', 'Password Changed Successfully') res.redirect('back')
}
from the Documentation Passport-local-mongoose ,you first need to get the specific user to update the password , here in my case the current login user , which is available on the req.user which we are exposed to , you can use any of the return property to query your collection, using async await i made a variable to hold the return object, in my case 'user', thereafter i chained the setProperty on it passing in the new password(req.body.password) since it return a promise i await it and assign a variable to it. from here you are good ...Note: since it is a promise it either resolved of reject, handling error can be done by rapping your code in a safe blanket, try..catch . You can read more Here
Since changePassword is a schema method, it must be used on an instance of a model, not the model itself or the imported passportLocalMongoose.
UserModel.findById(req.user._id)
// I assume you already have authentication and the req.user is generated
.then(foundUser => {
foundUser.changePassword(req.body.old, req.body.new)
.then(() => {
console.log('password changed');
})
.catch((error) => {
console.log(error);
})
})
.catch((error) => {
console.log(error);
});
the user object passport sends in callback function is just an object and not a schema instance document object, thus it does not have the changePassword function.
I'm trying to write a middleware function that (when a POST request is made with a username/password) checks to see if the user being created already exists in the database. I don't know if I'm doing this properly though.
User.find({ username: req.body.username }) returns an object which contains (or does not contain) the user if it exists...but how to properly return to exit if a user under the same username is found? Whenever I test this with Mocha, res.body.msg comes up as undefined.
Code:
module.exports = exports = function(req, res, next) {
User.find({ username: req.body.username }, (err, user) => {
if (err) return handleDBError(err, res);
if (user) return res.status(200).json({ msg: 'an account with this username already exists' });
});
next();
};
User Schema:
var userSchema = new mongoose.Schema({
username: String,
authentication: {
email: String,
password: String
}
});
give it a try very initial create a function to get the user response
function findUser(arg, callback) {
/* write your query here */
return callback(pass response here)
}
And then use it where you want
findUser(arg,function(callbackResponse) { /*do something*/ })
Since nodejs is asynchronous, chances are that the response is being sent after you are trying to read it. Make sure to keep the reading process waiting untill response is sent. I personaly use passport for handling that.