graphql-request icon indicating copy to clipboard operation
graphql-request copied to clipboard

Global error handling on a graphql-request instance

Open joeynimu opened this issue 4 years ago • 5 comments
trafficstars

Hi,

I am wondering if there is a way to handle errors globally?

For context, I am using graphql-request with graphql-code-gen SDK. I have a graphql-instance that I pass to the getSdk function i.e ;

   const graphqlRequestInstance = new GraphQLClient(
    `${process.env.NEXT_PUBLIC_BASE_API_URL}/${endpoint}`,
    {
      headers: {...},
    }
  );
  const sdk = getSdk(graphqlRequestInstance);
  ...

I have one instance so that I don't have to configure the URL repeatedly, plus some headers for auth among others.

One of the scenarios I have is that the request might throw a 401 if the session has expired, and I'd need to handle that. How would I handle that in this kind of setup globally?

For example, when using axios I could do something like this;

axiosInstance.interceptors.response.use(
  (response) => response,
  async (error: AxiosError) => {
    if (error?.response?.status === 401) {
            //handle the 401 error
    }
    return Promise.reject(error);
  }
);

joeynimu avatar Mar 10 '21 09:03 joeynimu

I was coming from Axios as well and resolved this with an intermediary class:

class GraphqlClient {
    private repository: Repository
    private client: GraphQLClient

    constructor(repository: Repository) {
        this.repository = repository
        this.client = new GraphQLClient("http://localhost:8080/graphql")
    }

    query<T = GraphqlQuery, V = Variables>(document: RequestDocument, variables?: V): Promise<T> {
        return this.makeRequest<T, V>(document, variables)
    }

    mutation<T = GraphqlMutation, V = Variables>(
        document: RequestDocument,
        variables?: V,
    ): Promise<T> {
        return this.makeRequest<T, V>(document, variables)
    }

    private makeRequest<T = GraphqlQuery, V = Variables>(
        document: RequestDocument,
        variables?: V,
    ): Promise<T> {
        return new Promise((resolve, reject) => {
            this.client
                .request(document, variables, {
                    Authorization: `Bearer ${this.repository.session.getJwt()}`,
                })
                .then(resolve)
                .catch((error: any) => {
                    const reqRes = JSON.parse(JSON.stringify(error))
                    return reject({ name: reqRes.response.errors[0].message })
                })
        })
    }
}

And then you can use it like:

graphqlClient.query(...)

and globally have errors resolved in the same way. I use this to add the jwt to every request as well, and eventually log some things to the server as well, hence why I've also split the query and mutation functions.

gabrielgatu avatar Mar 19 '21 09:03 gabrielgatu

@gabrielgatu, thanks for your response. Does your setup include using code-gen? If yes, is there a chance you can give a full example along with your custom graphql-client?

joeynimu avatar Mar 27 '21 07:03 joeynimu

@joeynimu There's an example of how to do just that in the documentation: https://www.graphql-code-generator.com/docs/plugins/typescript-graphql-request#examples-of-middleware

But it's using polly-js. I would like to use it without that as I'm not interested in retries.

dan-klasson avatar Apr 09 '21 18:04 dan-klasson

I also have this need

frei-x avatar Apr 21 '22 14:04 frei-x

You can override fetch client https://github.com/prisma-labs/graphql-request#using-a-custom-fetch-method, which allows you to use something like https://www.npmjs.com/package/fetch-retry instead.

gajus avatar Jun 21 '22 20:06 gajus