Custom hook to log request and errors while using either useQuery or useMutation?

Custom hook to log request and errors while using either useQuery or useMutation?


0

Requirement: Create a custom hook (useLoggedAPI) to call either useQuery or useMutation and add logs for request or errors. This utility function will be reused everywhere where there’s an GQL API call.

What I have tried so far:

/* eslint-disable @typescript-eslint/no-explicit-any */
import {
  useQuery,
  useMutation,
  AnyVariables,
  UseQueryArgs,
  DocumentInput,
  CombinedError,
  UseQueryResponse,
  UseMutationResponse,
} from 'urql';
import { logger } from '../../../../../logger/src';

type HookType = 'useQuery' | 'useMutation';

type ApiResponse<T extends HookType> = T extends 'useQuery'
  ? UseQueryResponse
  : UseMutationResponse;

type UseQueryParamType<
  TData = any,
  TVars extends AnyVariables = AnyVariables
> = UseQueryArgs<TVars, TData>;

type UseMutationParamType<
  TData = any,
  TVars extends AnyVariables = AnyVariables
> = DocumentInput<TData, TVars>;

type QueryParamsType = UseQueryParamType | UseMutationParamType;

const useLoggedAPI = <T extends HookType>(
  queryParams: QueryParamsType,
  hookType: T
): ApiResponse<T> => {
  let executeAPI: any;
  let APIName;
  let responseData;

  const handleError = (error: CombinedError | undefined) => {
    if (!error) return;
    if (error.graphQLErrors) {
      logger.error({
        event: 'api_account_graphQL_error',
        error: error.graphQLErrors,
        message: 'Account graphQL Error',
      });
      return;
    }
    if (error.networkError) {
      logger.error({
        event: 'api_account_network_error',
        error: error.networkError,
        message: 'Account network error',
      });
      return;
    }
    logger.error({
      event: 'api_account_error',
      error: error,
      message: 'Account error',
    });
  };

  switch (hookType) {
    case 'useQuery':
      APIName = 'Query';
      [responseData, executeAPI] = useQuery(queryParams as UseQueryParamType);
      handleError(responseData.error);
      break;

    case 'useMutation':
      APIName = 'Mutation';
      [responseData, executeAPI] = useMutation(
        queryParams as UseMutationParamType
      );
      handleError(responseData.error);
      break;

    default:
      throw new Error(`Invalid hook type specified: ${hookType}`);
  }

  const executeAPIWithLogging = async (params?: any) => {
    let result = undefined;
    try {
      if (executeAPI) {
        result = await executeAPI(params);
      }
    } catch (error: any) {
      handleError(error);
    }
    return result;
  };

  return [responseData, executeAPIWithLogging];
};

export default useLoggedAPI;

This would be called as such:

const queryParams = { query: 'mockQuery', variables: { id: 1 } };
const responseData = { data: { name: 'Mock Name' } };   

const result = useLoggedAPI(queryParams, 'useQuery');

Unfortunately, because of the design pattern – am getting this error:

error React Hook "useLoggedQuery" is called conditionally. React Hooks must be called in the exact same order in every component render.

Is this achievable? Any suggestions? (I don’t want to use any external libraries, as this should be fairly simple to do on our own.)


Load 5 more related questions


Show fewer related questions

0



Leave a Reply

Your email address will not be published. Required fields are marked *