|
| 1 | +# Error Handling |
| 2 | + |
| 3 | +If a request fails, the `error` property will be set to the response from the server, or the thrown error by `fetch`. This is the same as the `data` property for successful requests. |
| 4 | + |
| 5 | +The type of the `error` property on the React Query hooks will be set as `{ status: ...; body: ...; headers: ... } | Error`, where status is a non-2xx status code, and `body` |
| 6 | +set to your response schema for status codes defined in your contract, or `unknown` for status codes not in your contract. |
| 7 | + |
| 8 | +The `Error` type is included because requests can fail without returning a response. See [Fetch#Exceptions](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch#exceptions) for more information. |
| 9 | + |
| 10 | +```tsx |
| 11 | +import { isFetchError } from '@ts-rest/react-query/v5'; |
| 12 | +import { tsr } from './tsr'; |
| 13 | + |
| 14 | +const Post = ({ id }: { id: string }) => { |
| 15 | + const { data, error, isPending } = tsr.getPost.useQuery({ |
| 16 | + queryKey: ['posts', id], |
| 17 | + queryData: { |
| 18 | + params: { id }, |
| 19 | + }, |
| 20 | + }); |
| 21 | + |
| 22 | + if (isPending) { |
| 23 | + return <div>Loading...</div>; |
| 24 | + } |
| 25 | + |
| 26 | + if (error) { |
| 27 | + if (isFetchError(error)) { |
| 28 | + return <div>We could not retrieve this post. Please check your internet connection.</div>; |
| 29 | + } |
| 30 | + |
| 31 | + if (error.status === 404) { |
| 32 | + return <div>Post not found</div>; |
| 33 | + } |
| 34 | + |
| 35 | + return <div>Unexpected error occurred</div>; |
| 36 | + } |
| 37 | + |
| 38 | + return ( |
| 39 | + <div> |
| 40 | + <h1>{data.body.title}</h1> |
| 41 | + <p>{data.body.content}</p> |
| 42 | + </div> |
| 43 | + ); |
| 44 | +}; |
| 45 | +``` |
| 46 | + |
| 47 | +## Fully Type-Safe Error Handling |
| 48 | + |
| 49 | +In order to ensure that your code is handling all possible error cases, there are type guard functions that have been provided to help the handling of both expected and unexpected errors. |
| 50 | + |
| 51 | +- `isFetchError(error)` - Returns `true` if the error is an instance of `Error` thrown by `fetch`. |
| 52 | +- `isUnknownErrorResponse(error, contractEndpoint)` - Returns `true` if the error, if a response has been received but the status code is not defined in the contract. |
| 53 | +- `isNotKnownResponseError(error, contractEndpoint)` - Combines `isFetchError` and `isUnknownErrorResponse`, in case you want to be able to quickly type guard into defined error responses in one statement. |
| 54 | +- `exhaustiveGuard(error)` - Check if all possible error cases have been handled. Otherwise, you get a compile-time error. |
| 55 | + |
| 56 | +We also return the `contractEndpoint` property from all hooks, so you can easily pass it to the types guards without having import the contract. |
| 57 | + |
| 58 | +```tsx |
| 59 | +import { isFetchError, isUndefinedErrorResponse, exhaustiveGuard } from '@ts-rest/react-query/v5'; |
| 60 | +import { tsr } from './tsr'; |
| 61 | + |
| 62 | +const Post = ({ id }: { id: string }) => { |
| 63 | + const { data, error, isPending, contractEndpoint } = tsr.getPost.useQuery({ |
| 64 | + queryKey: ['posts', id], |
| 65 | + queryData: { |
| 66 | + params: { id }, |
| 67 | + }, |
| 68 | + }); |
| 69 | + |
| 70 | + if (isPending) { |
| 71 | + return <div>Loading...</div>; |
| 72 | + } |
| 73 | + |
| 74 | + if (error) { |
| 75 | + if (isFetchError(error)) { |
| 76 | + return <div>We could not retrieve this post. Please check your internet connection.</div>; |
| 77 | + } |
| 78 | + |
| 79 | + if (isUndefinedErrorResponse(error, contractEndpoint)) { |
| 80 | + return <div>Unexpected error occurred</div>; |
| 81 | + } |
| 82 | + |
| 83 | + if (error.status === 404) { |
| 84 | + return <div>Post not found</div>; |
| 85 | + } |
| 86 | + |
| 87 | + // this should be unreachable code if you handle all possible error cases |
| 88 | + // if not, you will get a compile-time error on the line below |
| 89 | + return exhaustiveGuard(error); |
| 90 | + } |
| 91 | + |
| 92 | + return ( |
| 93 | + <div> |
| 94 | + <h1>{data.body.title}</h1> |
| 95 | + <p>{data.body.content}</p> |
| 96 | + </div> |
| 97 | + ); |
| 98 | +}; |
| 99 | +``` |
0 commit comments