Home > Blockchain >  Async function sends a response before await returns a result
Async function sends a response before await returns a result

Time:09-17

I have a function that creates JWT-tokens and set them as cookies. I use this function in my login controller right before I send a response to a client. The problem is the controller sends a response before set-JWT-as-cookies function is finished.

controller

exports.login = catchAsync(async (req, res) => {
    const { username, password } = req.body;

    const user = await User.findOne({ username }).select(" password");

    if (!user || !(await bcrypt.compare(password, user.password))) {
        return res.status(401).json("wrong_credentials");
    }

    await jwtHelper.createTokens(user._id, res);

    console.log("right before sending a response", Date.now());

    res.status(200).json({
        status: "success",
        data: {
            role: user.role,
        },
    });
});

createTokens function

exports.createTokens = catchAsync(async (userId, res) => {
    const accessToken = createAccessToken(userId);
    const { tokenId: refreshTokenId, token: refreshToken } = createRefreshToken();

    await Token.create({ tokenId: refreshTokenId });

    const accessCookieOptions = {
        expires: new Date(
            Date.now()   process.env.JWT_COOKIE_EXPIRES_IN * 60 * 1000
        ),
        httpOnly: true,
        secure: true
    };

    const refreshCookieOptions = {
        expires: new Date(
            Date.now()  
                process.env.REFRESH_TOKEN_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000
        ),
        httpOnly: true,
        secure: true
    };

    console.log("right before creating cookies", Date.now());
    res.cookie("access-token", accessToken, accessCookieOptions);
    res.cookie("refresh-token", refreshToken, refreshCookieOptions);
    console.log("right after creating cookies", Date.now());
});

And that's what I get in a console

right before sending a response 1631544430242
[1] right before creating cookies 1631544430324
[1] (node:11224) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

How do I set cookies before sending the response?

CodePudding user response:

Because of the catchAsync, the createTokens function returns immediately even though it involves the asynchronous Token.create. After it returns, res.json is executed, and res.cookie comes only later, after the Token.create has also finished. This leads to the error.

The following illustrates this:

const {catchAsync} = require("catch-async-express");

async function f() {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve, 1000);
    }).then(() => console.log(1));
}

async function g() {
    await catchAsync(f)();
    console.log(2);
}

g();

2 is logged immediately, and 1 is logged after the 1000ms timeout.

Avoid using catchAsync here.

  • Related