angelguzmaning/ts-redux-react-realworld-example-app

should conduit.ts handle http error?

Closed this issue · 5 comments

I tried to play around this project.
It is awesome that you used typescript and decoders to check the data's type.

however, when the axios's request get a non-200 response, it also raise an error, then the data destructing for {data} will not work well.

I assume the purpose of try catch ({data}) {} is only used for catch the error from guard, is it right?

do I need to do something like this to handle the error ?

export async function login(email: string, password: string): Promise<Result<User, GenericErrors>> {
  try {
    const { data } = await axios.post('users/login', { user: { email, password } });

    return Ok(guard(object({ user: userDecoder }))(data).user);
  } catch (error) {
    if (error.response.status != 200) {
      return Err(guard(object({ errors: genericErrorsDecoder }))(error.response.data).errors);
    }else { // error from guard
      return Err(guard(object({ errors: genericErrorsDecoder }))(error.data).errors);
    }

    //return Err(guard(object({ errors: genericErrorsDecoder }))(data).errors);
  }
}

or do something like this ?

 await axios.post('users/login', { user: { email, password } }).catch(function(error){})

how do you think? what is the best practice to handle the errors?

checked the axios's doc ,
it provides a guard for typescript

so the code may look like this

export async function login(email: string, password: string): Promise<Result<User, GenericErrors>> {
  try {
    const { data } = await axios.post('users/login', { user: { email, password } });

    return Ok(guard(object({ user: userDecoder }))(data).user);
  } catch (error) {
    if (axios.isAxiosError(error)) {
      return Err(guard(object({ errors: genericErrorsDecoder }))(error?.response?.data).errors);
    } else {
      return Err(guard(object({ errors: genericErrorsDecoder }))(error.data).errors);
    }

    //return Err(guard(object({ errors: genericErrorsDecoder }))(data).errors);
  }
}

however I used error?.response?.data to get the compile to pass, which is not very typescript.
maybe you have better ideas as I am not quite familiar with typescript yet.

Hello @zztczcx
I'm not taking into consideration the non-200 errors (as you pointed out) and neither the possible error of wrong parsing of the generic error data. For the login function, I think the correct and complete type should be something like Promise<Result<User, Either<GenericError, ServerError, ParseError>>>, so if you could find an Either monad or create your own that could work.

My recommendations for handling the server error (non-200) would be to first wrap the requests (axios.post) in a function that returns a Promise<Result<unknown, ServerError>>. That way you can reuse the same server error catcher on all service functions.

Finally the login function would look something like this:

export async function login(email: string, password: string): Promise<Result<User, Either<GenericError, ServerError, ParseError>>> {
    const result = await wrapperPost('users/login', { user: { email, password } });
    return result.match({
        err: err => err,
        ok: data => {
            try {
               return Ok(guard(object({ user: userDecoder }))(data).user);
            } catch ({data}) {
              try {
                  return Err(guard(object({ errors: genericErrorsDecoder }))(data).errors
              } catch() { return Err(parseError) }
            }
        }
    })

So what is missing is to find or create a good Either monad. I also recommend checking the elm documentation as my inspiration for using typescript and decoders and making it as typesafe as possible comes from elm.

Thanks for your reply.

as for the 2xx and 4xx response, there will be response data, even though 4xx will be treated as error by axios.
so the wrappedPost's result will be Result<unknown> for 2xx and 4xx, and Result<ServerError> for other status like 500.

Am I right? do you welcome Pull request?

I will have a look at the elm and see how to wrap the axios.

have you used fluture ? I googled and found it, it seems provides some monad to handle asynchronous IO error.

I do welcome the pull request. About fluture I'm not sure how that one would help, I think the main issue is still the missing Either monad, so that we can handle multiple types of errors.