Home > Software engineering >  Create an object in a static function in Mongoose and handle its result from router
Create an object in a static function in Mongoose and handle its result from router

Time:06-28

I am having trouble understanding the use of await in Node with mongoose and express.

Basically I have a page /profile where I generate a token for every get request I have, according to the user that is connected:

router.get('/profile', requiresLogin, (req, res, next) => {
    User.findById(req.session.userId).exec(function(err, user){
        if(err || !user){
            //Display error and return
            return res.send("Error");
        }

        var token = Token.generateToken(user);
        console.log("Token is: "   token);
        return res.send("Working");
    }
})

The function to generate the token is the following:

TokenSchema.statics.generateToken = function(user){
    const newToken = new this({
        _userId: user._id,
        token: crypto.randomBytes(16).toString('hex')
    });
    await newToken.save();
    return newToken;
}

And I get an error on the await line saying: SyntaxError: await is only valid in async functions and the top level bodies of modules.

I think the code is quite intuitive, so what I want to accomplish is to have an static function to generate the Token objects. Then this function will return and other methods (like the one I posted that gets executed when getting /profile) can use it to create new tokens.

Is this the proper way to achieve what I am trying to achieve? I would like recommendations on how to do this code more "nodejs", since I am used to other languages where there are no promises and so on, and this is the best I could come up with. I know it is possible to do with callbacks and so on, but I think they might complicate the code for what it should be with other languages.

Thanks!

UPDATE: just after posting I saw that removing the await does what I expected, but as I have seen in here:

The save() method is asynchronous, so it returns a promise that you can await on.

So I don't really understand how nor why it is working

CodePudding user response:

You can't use await in a non-async funciton, which means that:

  • generateToken must be async.
  • When calling generateToken in the request handler, you must also await that call.
  • Again, you can't use await there without marking the anonymous handler function as async.

Your handler should look like this:

router.get("/profile", requiresLogin, (req, res, next) => {
    User.findById(req.session.userId).exec(async (err, user) => {
        if(err || !user) {
            return res.send("Error");
        }

        const token = await Token.generateToken(user);

        console.log("Token is: "   token);

        return res.send("Working");
    });
});

And the token generation function:

TokenSchema.statics.generateToken = async function(user) {
    const newToken = new this({
        _userId: user._id,
        token: crypto.randomBytes(16).toString('hex')
    });

    await newToken.save();

    return newToken;
}

You can also use async/await with Mongoose (instead of using callbacks), so your handler could look like this:

router.get("/profile", requiresLogin, async (req, res, next) => {
    try {
        const user = await User.findById(req.session.userId).exec();

        if (!user) return res.send("Can't find user.");

        const token = await Token.generateToken(user);

        console.log("Token is: "   token);

        return res.send("Working");       
    } catch (err) {
        return res.send("Error.");
    }
});
  • Related