Error Handling
If an error is thrown inside fetcher
, it will be returned as error
by the hook.
const fetcher = url => fetch(url).then(r => r.json())
// ...
const { data, error } = useCiteGraph('/api/user', fetcher)
The error
object will be defined if the fetch promise is rejected.
Status Code and Error Object
Sometimes we want an API to return an error object alongside the status code. Both of them are useful for the client.
We can customize our fetcher
to return more information. If the status code is not 2xx
,
we consider it an error even if it can be parsed as JSON:
const fetcher = async url => {
const res = await fetch(url)
// If the status code is not in the range 200-299,
// we still try to parse and throw it.
if (!res.ok) {
const error = new Error('An error occurred while fetching the data.')
// Attach extra info to the error object.
error.info = await res.json()
error.status = res.status
throw error
}
return res.json()
}
// ...
const { data, error } = useCiteGraph('/api/user', fetcher)
// error.info === {
// message: "You are not authorized to access this resource.",
// documentation_url: "..."
// }
// error.status === 403
Note that data
and error
can exist at the same time. So the UI can display the existing data,
while knowing the upcoming request has failed.
Here we have an example.
Error Retry
CiteGraph uses the exponential backoff algorithm (opens in a new tab) to retry the request on error. The algorithm allows the app to recover from errors quickly, but not waste resources retrying too often.
You can also override this behavior via the onErrorRetry option:
useCiteGraph('/api/user', fetcher, {
onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
// Never retry on 404.
if (error.status === 404) return
// Never retry for a specific key.
if (key === '/api/user') return
// Only retry up to 10 times.
if (retryCount >= 10) return
// Retry after 5 seconds.
setTimeout(() => revalidate({ retryCount }), 5000)
}
})
This callback gives you the flexibility to retry based on various conditions. You can also disable it by setting shouldRetryOnError: false
.
It's also possible to provide it via the Global Configuration context.
Global Error Report
You can always get the error
object inside the component reactively.
But in case you want to handle the error globally, to notify the UI to show a toast (opens in a new tab) or a snackbar (opens in a new tab), or report it somewhere such as Sentry (opens in a new tab),
there's an onError
event:
<CiteGraphConfig value={{
onError: (error, key) => {
if (error.status !== 403 && error.status !== 404) {
// We can send the error to Sentry,
// or show a notification UI.
}
}
}}>
<MyApp />
</CiteGraphConfig>