Skip to content

bombillazo/error-x

Repository files navigation

error-x

npm downloads npm npm

A smart, isomorphic, and type-safe error library for TypeScript applications. Provides excellent DX with intelligent error conversion, stack trace preservation, serialization support, and HTTP error presets.

Features

  • 🎯 Type-safe error handling with full TypeScript support
  • 🔄 Smart error conversion from various formats (API responses, strings, Error objects)
  • 👤 User-friendly messages separate from technical messages
  • 🔗 Error chaining with cause preservation and stack trace preservation
  • 📊 Flexible metadata for additional context
  • 📦 Serialization/deserialization for network transfer and storage
  • 🎨 HTTP error presets for all status codes (400-511)
  • 🌐 Isomorphic - works in Node.js and browsers
  • ⚙️ Global configuration for defaults and documentation URLs

Installation

pnpm add @bombillazo/error-x
# or
npm install @bombillazo/error-x
# or
yarn add @bombillazo/error-x

Requirements

  • Node.js: 18 or higher
  • TypeScript: 5.0 or higher (optional, but recommended)
  • Target Environment: ES2022+

This library uses modern JavaScript features and ES2022 APIs. For browser compatibility, ensure your build tool (e.g., Vite, webpack, esbuild) is configured to target ES2022 or transpile accordingly.

Warning

This library is currently in pre-v1.0 development. While we strive to minimize breaking changes, the API may evolve based on feedback and real-world usage. We recommend pinning to specific versions and reviewing release notes when updating.

Once we reach version 1.0, we plan to minimize API changes and follow semantic versioning.

Documentation

Documentation

Quick Start

import { ErrorX, http } from '@bombillazo/error-x'

// Simple usage
throw new ErrorX('Database connection failed')

// With options
throw new ErrorX({
  message: 'User authentication failed',
  name: 'AuthError',
  code: 'AUTH_FAILED',
  uiMessage: 'Please check your credentials and try again',
  metadata: { userId: 123, loginAttempt: 3 },
  source: 'auth-service'
})

// Using HTTP presets
throw new ErrorX(http[404])

// Customizing presets
throw new ErrorX({
  ...http[401],
  message: 'Session expired',
  metadata: { userId: 123 }
})

// Smart conversion from unknown errors
try {
  await someOperation()
} catch (error) {
  const errorX = ErrorX.from(error)
  throw errorX.withMetadata({ context: 'additional info' })
}

Documentation

Constructor

new ErrorX(input?: string | ErrorXOptions)

All parameters are optional. ErrorX uses sensible defaults:

Property Type Default Value Description
message string 'An error occurred' Technical error message (pass-through, no auto-formatting)
name string 'Error' Error type/title
code string | number Auto-generated from name or 'ERROR' Error identifier (auto-generated from name in UPPER_SNAKE_CASE)
uiMessage string | undefined undefined User-friendly message for display
cause ErrorXCause | Error | unknown undefined Original error that caused this (preserves full error chain)
metadata Record<string, unknown> | undefined undefined Additional context and data (flexible storage for any extra info)
type string | undefined undefined Error type for categorization (e.g., 'http', 'validation')
docsUrl string | undefined undefined or auto-generated Documentation URL for this specific error
source string | undefined undefined or from config Where the error originated (service name, module, component)
timestamp number Date.now() Unix epoch timestamp in milliseconds when error was created
stack string Auto-generated Stack trace with preservation and cleaning (inherited from Error)

HTTP Error Presets

ErrorX provides pre-configured error templates via the http export:

import { ErrorX, http } from '@bombillazo/error-x'

// Use preset directly
throw new ErrorX(http[404])
// Result: 404 error with message "Not found.", code "NOT_FOUND", etc.

// Override specific fields
throw new ErrorX({
  ...http[404],
  message: 'User not found',
  metadata: { userId: 123 }
})

// Add error cause
try {
  // some operation
} catch (originalError) {
  throw new ErrorX({
    ...http[500],
    cause: originalError,
    metadata: { operation: 'database-query' }
  })
}

Available Presets

All presets are indexed by HTTP status code (numeric keys) and include:

  • code: Error code in UPPER_SNAKE_CASE
  • name: Descriptive error name
  • message: Technical message with proper sentence casing and period
  • uiMessage: User-friendly message
  • metadata: Contains { status: <number> } with the HTTP status code

Creating Your Own Presets

HTTP presets work well because HTTP status codes are universally standardized. For domain-specific errors (database, validation, authentication, business logic), create your own presets:

import { type ErrorXOptions } from '@bombillazo/error-x'

// Define your application-specific presets
export const dbErrors = {
  connectionFailed: {
    name: 'DatabaseError',
    code: 'DB_CONNECTION_FAILED',
    message: 'Database connection failed.',
    uiMessage: 'Unable to connect to database. Please try again later.',
    type: 'database',
  },
  queryTimeout: {
    name: 'DatabaseError',
    code: 'DB_QUERY_TIMEOUT',
    message: 'Database query timeout.',
    uiMessage: 'The operation took too long. Please try again.',
    type: 'database',
  },
} satisfies Record<string, ErrorXOptions>;

export const authErrors = {
  invalidToken: {
    name: 'AuthenticationError',
    code: 'AUTH_INVALID_TOKEN',
    message: 'Invalid authentication token.',
    uiMessage: 'Your session has expired. Please log in again.',
    metadata: { status: 401 },
    type: 'authentication',
  },
  insufficientPermissions: {
    name: 'AuthorizationError',
    code: 'AUTH_INSUFFICIENT_PERMISSIONS',
    message: 'Insufficient permissions.',
    uiMessage: 'You do not have permission to perform this action.',
    metadata: { status: 403 },
    type: 'authorization',
  },
} satisfies Record<string, ErrorXOptions>;

// Use them just like http presets
throw new ErrorX(dbErrors.connectionFailed);
throw new ErrorX({ ...authErrors.invalidToken, metadata: { userId: 123 } });

This approach keeps your error handling consistent while remaining flexible for your specific domain.

Usage Examples

Basic Error Handling

import { ErrorX } from '@bombillazo/error-x'

function validateUser(user: unknown) {
  if (!user) {
    throw new ErrorX({
      message: 'User validation failed: user is required',
      name: 'ValidationError',
      code: 'USER_REQUIRED',
      uiMessage: 'Please provide user information',
      metadata: { field: 'user', received: user }
    })
  }
}

API Error Handling

async function fetchUser(id: string) {
  try {
    const response = await fetch(`/api/users/${id}`)
    if (!response.ok) {
      throw new ErrorX({
        ...http[response.status === 404 ? 404 : 500],
        metadata: { status: response.status, statusText: response.statusText }
      })
    }
    return response.json()
  } catch (error) {
    const errorX = ErrorX.from(error)
    throw errorX.withMetadata({ userId: id, operation: 'fetchUser' })
  }
}

Error Chaining

try {
  await database.transaction(async (tx) => {
    await tx.users.create(userData)
  })
} catch (dbError) {
  throw new ErrorX({
    message: 'User creation failed',
    name: 'UserCreationError',
    code: 'USER_CREATE_FAILED',
    uiMessage: 'Unable to create user account',
    cause: dbError, // Preserves original stack trace
    metadata: {
      operation: 'userRegistration',
      email: userData.email
    }
  })
}

Message Formatting

Important: ErrorX does NOT auto-format messages. Messages are passed through as-is:

new ErrorX({ message: 'test error' })
// message: 'test error' (exactly as provided)

new ErrorX({ message: 'Test error.' })
// message: 'Test error.' (exactly as provided)

Empty or whitespace-only messages default to 'An error occurred':

new ErrorX({ message: '' })
// message: 'An error occurred'

new ErrorX({ message: '   ' })
// message: 'An error occurred'

HTTP presets provide properly formatted messages with sentence casing and periods.

Auto Code Generation

Error codes are automatically generated from names when not provided:

new ErrorX({ message: 'Failed', name: 'DatabaseError' })
// code: 'DATABASE_ERROR'

new ErrorX({ message: 'Failed', name: 'userAuthError' })
// code: 'USER_AUTH_ERROR'

new ErrorX({ message: 'Failed', name: 'API Timeout' })
// code: 'API_TIMEOUT'

License

MIT

About

A simple and consistent error handling library for TypeScript applications

Topics

Resources

License

Stars

Watchers

Forks

Contributors 3

  •  
  •  
  •