Skip to content

Commit

Permalink
typesafe handleErrors (#808)
Browse files Browse the repository at this point in the history
  • Loading branch information
david-crespo authored Apr 17, 2022
1 parent 64464ce commit 0b04815
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 21 deletions.
30 changes: 14 additions & 16 deletions libs/api/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,21 @@ const methodCodeMap: { [key in keyof Partial<ApiMethods>]: Record<string, string
},
}

export const handleErrors =
<M>(method: M) =>
(resp: ErrorResponse) => {
// TODO is this a valid failure condition?
if (!resp) throw 'unknown server error'

// if logged out, hit /login to trigger login redirect
if (resp.status === 401) {
// TODO-usability: for background requests, a redirect to login without
// warning could come as a surprise to the user, especially because
// sometimes background requests are not directly triggered by a user
// action, e.g., polling or refetching when window regains focus
navToLogin({ includeCurrent: true })
}
// we need to rethrow because that's how react-query knows it's an error
throw formatServerError(resp, methodCodeMap[method as unknown as keyof ApiMethods])
export const handleErrors = (method: keyof ApiMethods) => (resp: ErrorResponse) => {
// TODO is this a valid failure condition?
if (!resp) throw 'unknown server error'

// if logged out, hit /login to trigger login redirect
if (resp.status === 401) {
// TODO-usability: for background requests, a redirect to login without
// warning could come as a surprise to the user, especially because
// sometimes background requests are not directly triggered by a user
// action, e.g., polling or refetching when window regains focus
navToLogin({ includeCurrent: true })
}
// we need to rethrow because that's how react-query knows it's an error
throw formatServerError(resp, methodCodeMap[method])
}

function formatServerError(
resp: ErrorResponse,
Expand Down
11 changes: 8 additions & 3 deletions libs/api/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {
QueryKey,
} from 'react-query'
import { useMutation, useQuery, useQueryClient } from 'react-query'
import { handleErrors } from './errors'

import type { ErrorResponse, ApiResponse } from './__generated__/Api'

Expand All @@ -29,7 +28,10 @@ type ApiClient = Record<string, (...args: any) => Promise<ApiResponse<any>>>
/* eslint-enable @typescript-eslint/no-explicit-any */

export const getUseApiQuery =
<A extends ApiClient>(api: A) =>
<A extends ApiClient>(
api: A,
handleErrors: (M: keyof A) => (resp: ErrorResponse) => void
) =>
<M extends keyof A>(
method: M,
params: Params<A[M]>,
Expand Down Expand Up @@ -65,7 +67,10 @@ export const getUseApiQuery =
}

export const getUseApiMutation =
<A extends ApiClient>(api: A) =>
<A extends ApiClient>(
api: A,
handleErrors: (M: keyof A) => (resp: ErrorResponse) => void
) =>
<M extends keyof A>(
method: M,
options?: UseMutationOptions<Result<A[M]>, ErrorResponse, Params<A[M]>>
Expand Down
5 changes: 3 additions & 2 deletions libs/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ResultItem } from './hooks'
import { getUseApiMutation, getUseApiQuery, getUseApiQueryClient } from './hooks'
import { handleErrors } from './errors'
import { Api } from './__generated__/Api'

const api = new Api({
Expand All @@ -20,8 +21,8 @@ export type ApiListMethods = {
: M]: ApiMethods[M]
}

export const useApiQuery = getUseApiQuery(api.methods)
export const useApiMutation = getUseApiMutation(api.methods)
export const useApiQuery = getUseApiQuery(api.methods, handleErrors)
export const useApiMutation = getUseApiMutation(api.methods, handleErrors)
export const useApiQueryClient = getUseApiQueryClient<ApiMethods>()

export * from './util'
Expand Down

0 comments on commit 0b04815

Please sign in to comment.