Error Handling
Error types
| Error | Package | Thrown when |
|---|---|---|
ValidationError | @routar/core | Request or response validation fails |
HttpError | @routar/core | Server returns a non-2xx status (fetch executor only) |
TimeoutError | @routar/core | Request exceeds the createFetchExecutor timeout |
AxiosError | axios | Network or HTTP error via Axios |
Handling errors
import { TimeoutError, ValidationError } from '@routar/core'
import { HttpError } from '@routar/core'
try {
await todoApi.create({ body: { title: '' } })
} catch (err) {
if (err instanceof ValidationError) {
// request or response schema failed
console.log(err.message)
console.log(err.cause) // original Zod/Valibot error
}
if (err instanceof HttpError) {
// fetch returned non-2xx
console.log(err.status) // e.g. 422
console.log(err.statusText) // e.g. 'Unprocessable Entity'
console.log(err.body) // parsed JSON payload, or null
}
if (err instanceof TimeoutError) {
console.log(err.ms) // configured timeout in ms
}
}Branching by executor type
Each transport throws a different error type on HTTP failures. If you’re switching executors or want to handle all HTTP errors in one place:
import { HttpError } from '@routar/core'
import { isAxiosError } from 'axios'
import { HTTPError } from 'ky'
try {
await todoApi.getDetail({ path: { id: 1 } })
} catch (err) {
if (err instanceof HttpError) {
// createFetchExecutor — non-2xx response
console.log(err.status, err.body)
} else if (isAxiosError(err)) {
// @routar/axios — Axios network or HTTP error
console.log(err.response?.status, err.response?.data)
} else if (err instanceof HTTPError) {
// @routar/ky — ky HTTP error
console.log(err.response.status, await err.response.json())
}
}Normalizing errors across transports
Each transport throws its own error type. If you want a single instanceof check across fetch and axios, add a normalizer plugin with an onError hook:
import { definePlugin, HttpError } from '@routar/core'
import { isAxiosError } from 'axios'
const errorNormalizer = definePlugin({
name: 'error-normalizer',
onError: async (err) => {
if (isAxiosError(err)) {
throw new HttpError(err.response?.status ?? 0, err.message)
}
throw err
},
})
const executor = createAxiosExecutor(instance, {
plugins: [errorNormalizer],
})Retry only on server errors
retry is an option on createFetchExecutor:
import { createFetchExecutor, HttpError } from '@routar/core'
createFetchExecutor('https://api.example.com', {
retry: {
count: 3,
shouldRetry: (err) => {
if (err instanceof HttpError) return err.status >= 500
return true // retry on network errors
},
},
})Last updated on