diff --git a/.vscode/settings.json b/.vscode/settings.json index 8484bcd..56f99db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "javascriptreact", "vue" ], - "eslint.enable": true + "eslint.enable": true, + "typescript.tsdk": "node_modules/typescript/lib" } diff --git a/packages/vue-apollo-composable/src/useMutation.ts b/packages/vue-apollo-composable/src/useMutation.ts index ca348eb..7cd8b4e 100644 --- a/packages/vue-apollo-composable/src/useMutation.ts +++ b/packages/vue-apollo-composable/src/useMutation.ts @@ -1,10 +1,11 @@ import { DocumentNode } from 'graphql' -import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode } from '@apollo/client/core' +import { MutationOptions, OperationVariables, FetchResult, TypedDocumentNode, ApolloError } from '@apollo/client/core' import { ref, onBeforeUnmount, isRef, Ref, getCurrentInstance } from 'vue-demi' import { useApolloClient } from './useApolloClient' import { ReactiveFunction } from './util/ReactiveFunction' import { useEventHook } from './util/useEventHook' import { trackMutation } from './util/loadingTracking' +import { toApolloError } from './util/toApolloError' /** * `useMutation` options for mutations that don't require `variables`. @@ -27,12 +28,12 @@ export type MutateFunction = (variables?: TVariables | null export interface UseMutationReturn { mutate: MutateFunction loading: Ref - error: Ref + error: Ref called: Ref onDone: (fn: (param: FetchResult, Record>) => void) => { off: () => void } - onError: (fn: (param: Error) => void) => { + onError: (fn: (param: ApolloError) => void) => { off: () => void } } @@ -47,11 +48,11 @@ export function useMutation< const vm = getCurrentInstance() const loading = ref(false) vm && trackMutation(loading) - const error = ref(null) + const error = ref(null) const called = ref(false) const doneEvent = useEventHook, Record>>() - const errorEvent = useEventHook() + const errorEvent = useEventHook() // Apollo Client const { resolveClient } = useApolloClient() @@ -94,11 +95,12 @@ export function useMutation< doneEvent.trigger(result) return result } catch (e) { - error.value = e + const apolloError = toApolloError(e) + error.value = apolloError loading.value = false - errorEvent.trigger(e) + errorEvent.trigger(apolloError) if (currentOptions.throws === 'always' || (currentOptions.throws !== 'never' && !errorEvent.getCount())) { - throw e + throw apolloError } } return null diff --git a/packages/vue-apollo-composable/src/useQuery.ts b/packages/vue-apollo-composable/src/useQuery.ts index 48c6828..29bef6e 100644 --- a/packages/vue-apollo-composable/src/useQuery.ts +++ b/packages/vue-apollo-composable/src/useQuery.ts @@ -20,6 +20,7 @@ import { FetchMoreOptions, ObservableSubscription, TypedDocumentNode, + ApolloError, } from '@apollo/client/core' import { throttle, debounce } from 'throttle-debounce' import { useApolloClient } from './useApolloClient' @@ -28,6 +29,7 @@ import { paramToRef } from './util/paramToRef' import { paramToReactive } from './util/paramToReactive' import { useEventHook } from './util/useEventHook' import { trackQuery } from './util/loadingTracking' +import { toApolloError } from './util/toApolloError' import type { CurrentInstance } from './util/types' @@ -58,7 +60,7 @@ export interface UseQueryReturn { result: Ref loading: Ref networkStatus: Ref - error: Ref + error: Ref start: () => void stop: () => void restart: () => void @@ -73,7 +75,7 @@ export interface UseQueryReturn { onResult: (fn: (param: ApolloQueryResult) => void) => { off: () => void } - onError: (fn: (param: Error) => void) => { + onError: (fn: (param: ApolloError) => void) => { off: () => void } } @@ -154,8 +156,8 @@ export function useQueryImpl< */ const result = ref() const resultEvent = useEventHook>() - const error = ref(null) - const errorEvent = useEventHook() + const error = ref(null) + const errorEvent = useEventHook() // Loading @@ -168,7 +170,7 @@ export function useQueryImpl< // SSR let firstResolve: (() => void) | undefined - let firstReject: ((error: Error) => void) | undefined + let firstReject: ((apolloError: ApolloError) => void) | undefined onServerPrefetch?.(() => { if (!isEnabled.value || (isServer && currentOptions.value?.prefetch === false)) return @@ -178,8 +180,8 @@ export function useQueryImpl< firstResolve = undefined firstReject = undefined } - firstReject = (error: Error) => { - reject(error) + firstReject = (apolloError: ApolloError) => { + reject(apolloError) firstResolve = undefined firstReject = undefined } @@ -258,15 +260,10 @@ export function useQueryImpl< processNextResult(queryResult) - // Result errors - // This is set when `errorPolicy` is `all` - if (queryResult.errors?.length) { - const e = new Error(`GraphQL error: ${queryResult.errors.map(e => e.message).join(' | ')}`) - Object.assign(e, { - graphQLErrors: queryResult.errors, - networkError: null, - }) - processError(e) + // ApolloQueryResult.error may be set at the same time as we get a result + // when `errorPolicy` is `all` + if (queryResult.error !== undefined) { + processError(queryResult.error) } else { if (firstResolve) { firstResolve() @@ -282,22 +279,25 @@ export function useQueryImpl< resultEvent.trigger(queryResult) } - function onError (queryError: any) { + function onError (queryError: unknown) { + // any error should already be an ApolloError, but we make sure + const apolloError = toApolloError(queryError) + processNextResult((query.value as ObservableQuery).getCurrentResult()) - processError(queryError) + processError(apolloError) if (firstReject) { - firstReject(queryError) + firstReject(apolloError) stop() } // The observable closes the sub if an error occurs resubscribeToQuery() } - function processError (queryError: any) { - error.value = queryError + function processError (apolloError: ApolloError) { + error.value = apolloError loading.value = false networkStatus.value = 8 - errorEvent.trigger(queryError) + errorEvent.trigger(apolloError) } function resubscribeToQuery () { diff --git a/packages/vue-apollo-composable/src/useSubscription.ts b/packages/vue-apollo-composable/src/useSubscription.ts index 01dcade..179f1ad 100644 --- a/packages/vue-apollo-composable/src/useSubscription.ts +++ b/packages/vue-apollo-composable/src/useSubscription.ts @@ -16,6 +16,7 @@ import { Observable, ObservableSubscription, TypedDocumentNode, + ApolloError, } from '@apollo/client/core' import { throttle, debounce } from 'throttle-debounce' import { ReactiveFunction } from './util/ReactiveFunction' @@ -26,6 +27,7 @@ import { useEventHook } from './util/useEventHook' import { trackSubscription } from './util/loadingTracking' import type { CurrentInstance } from './util/types' +import { toApolloError } from './util/toApolloError' export interface UseSubscriptionOptions < // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -45,7 +47,7 @@ type OptionsParameter = UseSubscriptionOptions { result: Ref loading: Ref - error: Ref + error: Ref start: () => void stop: () => void restart: () => void @@ -56,7 +58,7 @@ export interface UseSubscriptionReturn { onResult: (fn: (param: FetchResult, Record>) => void) => { off: () => void } - onError: (fn: (param: Error) => void) => { + onError: (fn: (param: ApolloError) => void) => { off: () => void } } @@ -119,8 +121,8 @@ export function useSubscription < const result = ref() const resultEvent = useEventHook>() - const error = ref(null) - const errorEvent = useEventHook() + const error = ref(null) + const errorEvent = useEventHook() const loading = ref(false) vm && trackSubscription(loading) @@ -157,10 +159,12 @@ export function useSubscription < resultEvent.trigger(fetchResult) } - function onError (fetchError: any) { - error.value = fetchError + function onError (fetchError: unknown) { + const apolloError = toApolloError(fetchError) + + error.value = apolloError loading.value = false - errorEvent.trigger(fetchError) + errorEvent.trigger(apolloError) } function stop () { diff --git a/packages/vue-apollo-composable/src/util/toApolloError.ts b/packages/vue-apollo-composable/src/util/toApolloError.ts new file mode 100644 index 0000000..6c5d27d --- /dev/null +++ b/packages/vue-apollo-composable/src/util/toApolloError.ts @@ -0,0 +1,16 @@ +import { ApolloError, isApolloError } from '@apollo/client' + +export function toApolloError (error: unknown): ApolloError { + if (!(error instanceof Error)) { + return new ApolloError({ + networkError: Object.assign(new Error(), { originalError: error }), + errorMessage: String(error), + }) + } + + if (isApolloError(error)) { + return error + } + + return new ApolloError({ networkError: error, errorMessage: error.message }) +}