How to catch error of rejected promise on client side?

Viewed 18

I am trying to have my server reject the signup request if the user tries to sign up with an existing account. However, I cant seem to reject it properly and pass the error message to my client side.

//server.js

app.post('/signup', (req, res) => {
  const email = req.body.email
  const plainTextPassword = req.body.password;

  //check if user already exists 
  User.find({ email: email }, (err, existingUser) => {
    //account doesnt exist
    if (existingUser.length === 0) {
      bcrypt.hash(plainTextPassword, saltRounds, async (err, hash) => {
        try {
          const user = new User({
            email: email,
            password: hash
          });
          let result = await user.save();
          if (result) {
            res.send(result)
          }

        } catch (e) {
          res.send(e);
        }
      })
    } else {
      //notify user that account exists
      return Promise.reject(new Error('Account already exists'))
    }
  })

})
//reduxSlice.js
export const signup = createAsyncThunk(
    'userAuth/signup',
    async (payload, thunkAPI) => {
        const { email, password } = payload
        try {
            const result = await fetch(
                signupPath, {
                mode: 'cors',
                credentials: 'include',
                method: "post",
                body: JSON.stringify({ email, password }),
                headers: {
                    'Content-Type': 'application/json'
                }
            }
            )
            return result.json()
        } catch (error) {
            console.log(error) //this line executes
        }

    }
)

From my reduxdev tools, my signup is still fulfilled EVEN though I rejected it from my server. Also, my server crashes after one attempt, which leads me to suspect there is an uncaught error.

1 Answers

The client only receives what you do res.send() with or next(err) which will then call res.send(). Promises are local only to the server and are not something that gets sent back to the client.

In your original code, I'd suggest that you use ONLY promise-based asynchronous operations and then you can throw in your code, have one place to catch all the errors and then send an error back to the client from there.

class ServerError extends Error {
    constructor(msg, status) {
        super(msg)
        this.status = status;
    }
}

app.post('/signup', (req, res) => {
    try {
        const email = req.body.email
        const plainTextPassword = req.body.password;

        //check if user already exists
        const existingUser = await User.find({ email: email });
        //account doesnt exist
        if (existingUser.length !== 0) {
            throw new ServerError('Account already exist', 403);
        }
        const hash = await bcrypt.hash(plainTextPassword, saltRounds);
        const user = new User({
            email: email,
            password: hash
        });
        const result = await user.save();
        res.send(result);
    } catch (e) {
        if (!e.status) e.status = 500;
        console.log(e);
        res.status(e.status).send({err: e.message});
    }
});

Then, in your client code that is using fetch(), you need to check result.ok to see if you got a 2xx status back or not. fetch() only rejects if the network connection to the target host failed. If the connection succeeded, even if it returns an error status, the fetch() promise will resolve. You have to examine result.ok to see if you got a 2xx status or not.

//reduxSlice.js
export const signup = createAsyncThunk(
    'userAuth/signup',
    async (payload, thunkAPI) => {
        const { email, password } = payload
        try {
            const result = await fetch(
                signupPath, {
                mode: 'cors',
                credentials: 'include',
                method: "post",
                body: JSON.stringify({ email, password }),
                headers: {
                    'Content-Type': 'application/json'
                }
            });
            // check to see if we got a 2xx status success
            if (!result.ok) {
                throw new Error(`signup failed: ${response.status}`);
            }
            return result.json()
        } catch (error) {
            console.log(error) //this line executes
        }

    }
)
Related