Guide
Error Handling
Comprehensive error handling and custom error types.
The library provides rich error types with detailed context about failed requests, making debugging and error handling straightforward.
Error Types
All HTTP errors extend from RequestError:
import { errors } from '@outloud/reqo'
// Base error class
errors.RequestError
// Specific error types
errors.TimeoutError // Request timed out
errors.CanceledError // Request was canceled
Checking Errors
Using isError Method
The recommended way to check for HTTP errors:
import { reqo } from '@outloud/reqo'
try {
const data = await reqo.$get('/users/999')
} catch (error) {
if (reqo.isError(error)) {
// error is typed as RequestError
console.log('Status:', error.status)
console.log('Message:', error.message)
console.log('URL:', error.url)
}
}
Using instanceof
Check for specific error types:
import { errors } from '@outloud/reqo'
try {
const data = await reqo.$get('/users', {}, { timeout: 1000 })
} catch (error) {
if (error instanceof errors.TimeoutError) {
console.log('Request timed out')
} else if (error instanceof errors.CanceledError) {
console.log('Request was canceled')
} else if (error instanceof errors.RequestError) {
console.log('Other request error:', error.status)
}
}
Error Properties
message
string
Error message describing what went wrong.
error.message // 'Request failed with status code 404.'
status
number
HTTP status code of the response. Defaults to
-1 if no response was received.error.status // 404, 500, -1, etc.
code
string
Error code. Empty string for HTTP errors, or network error codes like 'ETIMEDOUT', 'ECONNREFUSED'.
error.code // '', 'E_TIMEOUT', 'E_CANCELED', 'ECONNRESET'
url
string
The request URL that failed.
error.url // 'https://api.example.com/users/999'
method
string
HTTP method used for the request.
error.method // 'GET', 'POST', etc.
params
Params | undefined
Query parameters sent with the request.
error.params // { id: 123, status: 'active' }
data
any
Response body data if available.
error.data // { error: { message: 'Not found' } }
client
string | undefined
Client ID if one was set.
error.client // 'github-api'
config
RequestConfig
Full request configuration (hidden from enumeration but accessible).
error.config.headers
error.config.timeout
request
Request
The Request object passed to fetch (hidden from enumeration).
error.request.headers
response
Response | undefined
The Response object if one was received (hidden from enumeration).
error.response?.headers
error.response?.status
cause
Error | undefined
Original error that caused this error.
error.cause // Original network error
Error Serialization
Errors can be serialized to JSON:
try {
await reqo.$get('/users/999', { search: 'test' })
} catch (error) {
if (reqo.isError(error)) {
const json = error.toJSON()
console.log(json)
/*
{
client: 'api-client',
url: 'https://api.example.com/users/999',
params: { search: 'test' },
method: 'GET',
message: 'Request failed with status code 404.',
code: '',
status: 404,
data: { error: { message: 'User not found' } }
}
*/
}
}
Status Code Handling
Handle different HTTP status codes:
try {
const user = await reqo.$get('/users/123')
} catch (error) {
if (!reqo.isError(error)) throw error
switch (error.status) {
case 400:
console.error('Bad request:', error.data)
break
case 401:
console.error('Unauthorized - please login')
// Redirect to login
break
case 403:
console.error('Forbidden - insufficient permissions')
break
case 404:
console.error('User not found')
break
case 429:
console.error('Rate limited - try again later')
const retryAfter = error.response?.headers.get('Retry-After')
console.log(`Retry after ${retryAfter} seconds`)
break
case 500:
case 502:
case 503:
console.error('Server error - try again')
break
default:
console.error('Unexpected error:', error.status)
}
}
Network Error Handling
Handle network-level errors:
try {
const data = await reqo.$get('https://api.example.com/data')
} catch (error) {
if (!reqo.isError(error)) throw error
switch (error.code) {
case 'ECONNREFUSED':
console.error('Connection refused - server may be down')
break
case 'ETIMEDOUT':
console.error('Connection timed out')
break
case 'ENOTFOUND':
console.error('DNS lookup failed - check the URL')
break
case 'ECONNRESET':
console.error('Connection reset by server')
break
case 'E_TIMEOUT':
console.error('Request timeout exceeded')
break
case 'E_CANCELED':
console.error('Request was canceled')
break
default:
if (error.status === -1) {
console.error('Network error:', error.message)
}
}
}
Response Data Access
Handle API validation errors:
interface ValidationError {
field: string
message: string
}
interface ApiError {
message: string
errors?: ValidationError[]
}
try {
await reqo.$post<User, CreateUserDto>('/users', {
name: '',
email: 'invalid'
})
} catch (error) {
if (reqo.isError(error) && error.status === 422) {
const apiError = error.data as ApiError
apiError.errors?.forEach((err) => {
console.error(`${err.field}: ${err.message}`)
})
}
}
Global Error Handler
Set up a global error handler:
import { createClient } from '@outloud/reqo'
const client = createClient({
url: 'https://api.example.com'
})
client.on('error', (error) => {
// Log all errors
console.error('API Error:', {
url: error.url,
method: error.method,
status: error.status,
message: error.message
})
// Send to error tracking service
if (error.status >= 500) {
errorTracker.captureException(error)
}
// Show user-friendly messages
if (error.status === 401) {
toast.error('Please log in to continue')
} else if (error.status >= 500) {
toast.error('Server error. Please try again later.')
}
})
Best Practices
- Always check error types: Use
reqo.isError()orinstanceofto type-check errors - Handle specific status codes: Provide user-friendly messages for common errors
- Log errors: Send error details to monitoring services
- Serialize carefully: Use
error.toJSON()for logging, but be aware it may contain sensitive data - Don't swallow errors: Re-throw unexpected errors
- Provide fallbacks: Return cached data or defaults when appropriate
- User-friendly messages: Transform technical errors into actionable messages