openapistack / openapi-client-axios

JavaScript client library for consuming OpenAPI-enabled APIs with axios
https://openapistack.co
MIT License
558 stars 67 forks source link

A bit confused on how to use for error responses #97

Closed albseb511 closed 2 years ago

albseb511 commented 2 years ago

I have got it to work for getting general responses back

// schema imported auto-generated by openapi-typescript.
// So i am using openapi-typescript and axios client, it is probably wrong to use both, i just did not want things to break right now :)

const type Response = components["schemas"]["Response"];

export const doSomething = async ( ): Promise<Response> => {
  const client = await api.client;
  const response = await client.make_some_request(skug,null,{
      params:{
       ...
      }
  });
  return response.data;
};

This works fine for correct data responses, how do I manage error ones?

I looked at an example for

Awaited<ReturnType<Client["something"]>>["data"]

where do i get the type for error ones?

I can see there are 400 etc codes generated in the schema files though

albseb511 commented 2 years ago

For now, I have just used this gist since i can still use typescript,

https://kentcdodds.com/blog/get-a-catch-block-error-message-with-typescript
type ErrorWithMessage = {
  message: string
}

function isErrorWithMessage(error: unknown): error is ErrorWithMessage {
  return (
    typeof error === 'object' &&
    error !== null &&
    'message' in error &&
    typeof (error as Record<string, unknown>).message === 'string'
  )
}

function toErrorWithMessage(maybeError: unknown): ErrorWithMessage {
  if (isErrorWithMessage(maybeError)) return maybeError

  try {
    return new Error(JSON.stringify(maybeError))
  } catch {
    // fallback in case there's an error stringifying the maybeError
    // like with circular references for example.
    return new Error(String(maybeError))
  }
}

function getErrorMessage(error: unknown) {
  return toErrorWithMessage(error).message
}
npdev453 commented 2 years ago

Currently, types-schema can't provide rejection type. (Without any extra wrappers or modified Promise type)

Axios throws all non-200 responses and Typescript do not allow to make it typed https://github.com/microsoft/TypeScript/issues/6283#issuecomment-240804072

Also, for now you can try:

import { Paths } from './petshop.d.ts'

type PetsPutRejectionType = Paths.PetsPut.Responses.$500
albseb511 commented 2 years ago

Thanks will look into this

I did end up using this instead


  export function axiosErrorHandler(error: unknown){
    if( axios.isAxiosError(error) ){
      if( error.response?.data ){
        return error.response.data as { message: string } | { detail: { loc: string[] }, msg: string, type: string }[] ;
      }
    }
    else { 
      return {
        message: getErrorMessage(error)
      }
    }
  }

do you think using axios.isAxiosError internally will help in typing correctly?

catch (err) {
      const e =  axiosErrorHandler(err)
      if( typeof e ==="object" && "message" in e){
        snackbar.error(e.message);
      }
      else{
        snackbar.error("something went wrong");
      }
      setLoading(false);
      return;
    }
npdev453 commented 2 years ago

About your question: https://github.com/axios/axios/blob/master/index.d.ts#L216 https://github.com/axios/axios/blob/cd7ff042b0b80f6f02e5564d184019131c90cacd/lib/core/enhanceError.js#L21 Looking at sources I see that isAxiosError() assign to error an AxiosError type by is keyword. (TypeGuard)

But for getting more advantages and getting more concrete type you can try to write something like this Using TypeGuard + Generic Types:


function isAn403Error<T> (error: unknown) : error is AxiosError<T>  { 
    return axios.isAxiosError(error) && response.code === '403';
}

function isAn501Error <T> (error: unknown) : error is AxiosError<T>  { 
    return axios.isAxiosError(error) && response.code === '501';
}

function putPetRequest() {
     try {
         client.putPet()
     } catch (e) {
         if (isAn501Error<Paths.PetsPut.Responses.$501>(e)) {
             // TypeGuard will makes there type "e" there to $501
             snackbar.error(e.message);
         } else if (isAn403Error<Paths.PetsPut.Responses.$403>(e)) {
             snackbar.error(e.accesErrorMessages);
         } else if (axios.isAxiosError(error)) {
             // another http error
         } else {
            // non HTTP error
         }
     }
}
anttiviljami commented 2 years ago

Huge thanks @npdev453 for the support here 💪