Skip to content

Error Handling ​

Lucid Resourceful provides a comprehensive error handling system with structured exceptions for different types of failures. All errors extend the AdonisJS Exception class and include detailed error information, HTTP status codes, and type-safe error checking capabilities.

Overview ​

The error system in Lucid Resourceful is designed to provide:

  • Structured Error Information: All errors include error codes, HTTP status codes, and detailed messages
  • Type-Safe Error Checking: Use type guards to identify specific error types
  • Rich Context: Validation errors include Joi validation details
  • Consistent HTTP Status Codes: Proper status codes for different error scenarios
  • Developer-Friendly Messages: Clear, actionable error messages for debugging

Importing Errors ​

Errors can be imported in two ways:

From the Root Module ​

typescript
import { errors } from '@nhtio/lucid-resourceful'

// Use specific error classes
const { E_FORBIDDEN, E_INVALID_PAYLOAD_EXCEPTION } = errors

try {
  // Some operation that might fail
} catch (error) {
  if (error instanceof errors.E_FORBIDDEN) {
    // Handle forbidden access
  }
}

From the Errors Sub-Module ​

typescript
import {
  E_FORBIDDEN,
  E_INVALID_PAYLOAD_EXCEPTION,
  E_RECORD_NOT_FOUND_EXCEPTION
} from '@nhtio/lucid-resourceful/errors'

try {
  // Some operation that might fail
} catch (error) {
  if (error instanceof E_FORBIDDEN) {
    // Handle forbidden access
  }
}

Error Categories ​

Lucid Resourceful errors are organized into several categories based on their context and purpose:

Configuration and Setup Errors ​

Errors that occur during model setup, decorator configuration, or data type instantiation.

Validation Errors ​

Errors related to data validation, both at the model level and request payload level.

Access Control Errors ​

Errors thrown when access control restrictions prevent operations.

Query and Database Errors ​

Errors related to database queries, Lucene query parsing, and missing records.

Type Casting Errors ​

Errors thrown during data type conversion and casting operations.

Error Types ​

Configuration Errors ​

E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS ​

Thrown when invalid options are provided to data type constructors.

typescript
import { E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS } from '@nhtio/lucid-resourceful/errors'
import { ResourcefulStringType } from '@nhtio/lucid-resourceful/definitions'

try {
  const stringType = new ResourcefulStringType({
    minLength: -1, // Invalid: negative length
    maxLength: 5
  })
} catch (error) {
  if (error instanceof E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS) {
    console.error('Invalid data type options:', error.message)
    console.error('Validation details:', error.details)
    // Error includes Joi validation details
  }
}

Properties:

  • status: 500
  • code: 'E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS'
  • details: Joi validation error details (if available)

E_INVALID_RESOURCEFUL_MIXIN_OPTIONS ​

Thrown when invalid options are provided to the withResourceful mixin.

typescript
import { E_INVALID_RESOURCEFUL_MIXIN_OPTIONS } from '@nhtio/lucid-resourceful/errors'
import { withResourceful } from '@nhtio/lucid-resourceful'

try {
  class User extends withResourceful({
    name: 123, // Invalid: name must be string
    accessControlFilters: 'invalid' // Invalid: must be object
  })(BaseModel) {}
} catch (error) {
  if (error instanceof E_INVALID_RESOURCEFUL_MIXIN_OPTIONS) {
    console.error('Invalid mixin options:', error.message)
    console.error('Validation details:', error.details)
  }
}

Properties:

  • status: 500
  • code: 'E_INVALID_RESOURCEFUL_MIXIN_OPTIONS'
  • details: Joi validation error details (if available)

E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS ​

Thrown when invalid options are provided to resourceful decorators.

typescript
import { E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS } from '@nhtio/lucid-resourceful/errors'
import { resourcefulColumn } from '@nhtio/lucid-resourceful'

try {
  class User extends BaseModel {
    @resourcefulColumn({
      type: 'invalid-type', // Invalid data type
      validate: 'not-a-schema' // Invalid validation schema
    })
    declare name: string
  }
} catch (error) {
  if (error instanceof E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS) {
    console.error(`Invalid decorator options for field "${error.fieldName}":`, error.message)
    console.error('Validation details:', error.details)
  }
}

Properties:

  • status: 500
  • code: 'E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS'
  • details: Joi validation error details (if available)

Validation Errors ​

E_INVALID_PAYLOAD_EXCEPTION ​

Thrown when request payload validation fails during create or update operations.

typescript
import { E_INVALID_PAYLOAD_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  const user = await User.$onResourcefulCreate({
    name: '', // Invalid: empty name
    email: 'invalid-email' // Invalid: not a valid email format
  }, ctx, app)
} catch (error) {
  if (error instanceof E_INVALID_PAYLOAD_EXCEPTION) {
    console.error('Payload validation failed:', error.message)
    console.error('Validation details:', error.details)
    
    // Return structured error response
    return response.status(error.status).json({
      error: error.code,
      message: error.message,
      details: error.details
    })
  }
}

Properties:

  • status: 422
  • code: 'E_INVALID_PAYLOAD_EXCEPTION'
  • details: Joi validation error details (if available)

E_FORBIDDEN_PAYLOAD_EXCEPTION ​

Thrown when payload validation fails due to authorization constraints.

typescript
import { E_FORBIDDEN_PAYLOAD_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  const user = await User.$onResourcefulCreate({
    name: 'John Doe',
    role: 'admin' // Forbidden: regular users cannot set admin role
  }, ctx, app)
} catch (error) {
  if (error instanceof E_FORBIDDEN_PAYLOAD_EXCEPTION) {
    console.error('Payload authorization failed:', error.message)
    console.error('Validation details:', error.details)
    
    return response.status(error.status).json({
      error: error.code,
      message: 'You are not authorized to perform this action'
    })
  }
}

Properties:

  • status: 403
  • code: 'E_FORBIDDEN_PAYLOAD_EXCEPTION'
  • details: Joi validation error details (if available)

E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION ​

Thrown when request parameters for index/list operations fail validation.

typescript
import { E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  const result = await User.$onResourcefulIndex(
    'invalid-lucene-query[]', // Invalid Lucene syntax
    -1, // Invalid page number
    0, // Invalid perPage value
    ['nonexistent_field'], // Invalid field name
    ctx,
    app
  )
} catch (error) {
  if (error instanceof E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION) {
    console.error('Index request validation failed:', error.message)
    console.error('Validation details:', error.details)
  }
}

Properties:

  • status: 422
  • code: 'E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION'
  • details: Joi validation error details (if available)

Access Control Errors ​

E_FORBIDDEN ​

Thrown when access control filters deny access to operations.

typescript
import { E_FORBIDDEN } from '@nhtio/lucid-resourceful/errors'

try {
  const user = await User.$onResourcefulRead(123, ctx, app)
} catch (error) {
  if (error instanceof E_FORBIDDEN) {
    console.error('Access denied:', error.message)
    
    return response.status(error.status).json({
      error: error.code,
      message: 'You do not have permission to access this resource'
    })
  }
}

Properties:

  • status: 403
  • code: 'E_FORBIDDEN'
  • details: Error message string

E_INVALID_COLUMN_ACCESS ​

Thrown when trying to access columns that don't exist or are not accessible.

typescript
import { E_INVALID_COLUMN_ACCESS } from '@nhtio/lucid-resourceful/errors'

try {
  const result = await User.$onResourcefulIndex(
    'name:john',
    1,
    10,
    ['password', 'secret_key'], // Fields not accessible
    ctx,
    app
  )
} catch (error) {
  if (error instanceof E_INVALID_COLUMN_ACCESS) {
    console.error('Column access denied:', error.message)
    // Might be: "You don't have access to column "password" or the column does not exist."
  }
}

Properties:

  • status: 403
  • code: 'E_INVALID_COLUMN_ACCESS'
  • details: Error message string

Query and Database Errors ​

E_RECORD_NOT_FOUND_EXCEPTION ​

Thrown when a requested record cannot be found or accessed.

typescript
import { E_RECORD_NOT_FOUND_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  const user = await User.$onResourcefulRead(99999, ctx, app) // Non-existent ID
} catch (error) {
  if (error instanceof E_RECORD_NOT_FOUND_EXCEPTION) {
    console.error('Record not found:', error.message)
    
    return response.status(error.status).json({
      error: error.code,
      message: 'The requested resource was not found'
    })
  }
}

Properties:

  • status: 404
  • code: 'E_RECORD_NOT_FOUND_EXCEPTION'

E_MISSING_PRIMARY_KEY_EXCEPTION ​

Thrown when operations require a primary key but the model doesn't have one defined.

typescript
import { E_MISSING_PRIMARY_KEY_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  // Model without a primary key
  class Settings extends withResourceful()(BaseModel) {
    // No primary key defined
  }
  
  const result = await Settings.$onResourcefulRead(1, ctx, app)
} catch (error) {
  if (error instanceof E_MISSING_PRIMARY_KEY_EXCEPTION) {
    console.error('Model configuration error:', error.message)
    // Message: "Settings" does not have a primary key defined
  }
}

Properties:

  • status: 503
  • code: 'E_MISSING_PRIMARY_KEY_EXCEPTION'

E_INVALID_LUCENE_QUERY ​

Thrown when Lucene query syntax is invalid.

typescript
import { E_INVALID_LUCENE_QUERY } from '@nhtio/lucid-resourceful/errors'

try {
  const result = await User.$onResourcefulIndex(
    'name:[invalid syntax', // Unclosed bracket
    1,
    10,
    ['id', 'name'],
    ctx,
    app
  )
} catch (error) {
  if (error instanceof E_INVALID_LUCENE_QUERY) {
    console.error('Invalid query syntax:', error.message)
    // Provide user-friendly feedback about query syntax
  }
}

Properties:

  • status: 400
  • code: 'E_INVALID_LUCENE_QUERY'
  • details: Error message string

E_LUCENE_SYNTAX_EXCEPTION ​

Thrown when Lucene query parsing encounters syntax errors with detailed location information.

typescript
import { E_LUCENE_SYNTAX_EXCEPTION } from '@nhtio/lucid-resourceful/errors'

try {
  const result = await User.$onResourcefulIndex(
    'name:john AND (email:', // Unclosed parenthesis
    1,
    10,
    ['id', 'name'],
    ctx,
    app
  )
} catch (error) {
  if (error instanceof E_LUCENE_SYNTAX_EXCEPTION) {
    console.error('Query syntax error:', error.message)
    console.error(`Error at line ${error.line}, column ${error.column}, offset ${error.offset}`)
    
    // Provide precise error location for debugging
    return response.status(error.status).json({
      error: error.code,
      message: 'Invalid query syntax',
      location: {
        line: error.line,
        column: error.column,
        offset: error.offset
      }
    })
  }
}

Properties:

  • status: 422
  • code: 'E_LUCENE_SYNTAX_EXCEPTION'
  • offset: Error offset in the query string
  • line: Line number where error occurred
  • column: Column number where error occurred

E_UNEXPECTED_COLUMN_IN_QUERY ​

Thrown when Lucene queries reference columns that don't exist in the model.

typescript
import { E_UNEXPECTED_COLUMN_IN_QUERY } from '@nhtio/lucid-resourceful/errors'

try {
  const result = await User.$onResourcefulIndex(
    'nonexistent_field:value', // Field doesn't exist
    1,
    10,
    ['id', 'name'],
    ctx,
    app
  )
} catch (error) {
  if (error instanceof E_UNEXPECTED_COLUMN_IN_QUERY) {
    console.error('Unknown column in query:', error.message)
    // Message: Unexpected column in query: "nonexistent_field"
  }
}

Properties:

  • status: 400
  • code: 'E_UNEXPECTED_COLUMN_IN_QUERY'
  • details: Error message string

Type Casting Errors ​

E_UNCASTABLE ​

Thrown when values cannot be cast to the expected data type.

typescript
import { E_UNCASTABLE } from '@nhtio/lucid-resourceful/errors'

try {
  // This error typically occurs internally during data processing
  const value = castValueAsNumber('not-a-number')
} catch (error) {
  if (error instanceof Error && error.code === 'E_UNCASTABLE') {
    console.error('Type casting failed:', error.message)
    // Message: The value could not be cast to a valid number
  }
}

Properties:

  • status: 500
  • code: 'E_UNCASTABLE'

E_INVALID_PREPARED_VALUE ​

Thrown when values fail preparation for database storage.

typescript
import { E_INVALID_PREPARED_VALUE } from '@nhtio/lucid-resourceful/errors'

try {
  // This error typically occurs during model save operations
  user.email = 'invalid-email-format'
  await user.save()
} catch (error) {
  if (error instanceof Error && error.code === 'E_INVALID_PREPARED_VALUE') {
    console.error('Value preparation failed:', error.message)
    // Message: The value for "email" is not a valid email
  }
}

Properties:

  • status: 422
  • code: 'E_INVALID_PREPARED_VALUE'

E_INVALID_CONSUMED_VALUE ​

Thrown when values cannot be consumed from database results.

typescript
import { E_INVALID_CONSUMED_VALUE } from '@nhtio/lucid-resourceful/errors'

try {
  // This error typically occurs during model hydration
  const user = await User.find(1)
} catch (error) {
  if (error instanceof Error && error.code === 'E_INVALID_CONSUMED_VALUE') {
    console.error('Value consumption failed:', error.message)
    // Message: The value for "birth_date" could not be consumed as a valid date
  }
}

Properties:

  • status: 500
  • code: 'E_INVALID_CONSUMED_VALUE'

Type Guards for Error Checking ​

Lucid Resourceful provides type guards to safely check error types and access their properties.

Using isInstanceOf Type Guard ​

The isInstanceOf type guard provides a safe way to check error types, especially useful when dealing with bundled or transformed code where instanceof might not work reliably.

typescript
import { guards } from '@nhtio/lucid-resourceful/utils'
import { 
  E_FORBIDDEN, 
  E_INVALID_PAYLOAD_EXCEPTION,
  E_RECORD_NOT_FOUND_EXCEPTION 
} from '@nhtio/lucid-resourceful/errors'

try {
  const user = await User.$onResourcefulCreate(payload, ctx, app)
} catch (error) {
  // Using isInstanceOf for reliable type checking
  if (guards.isInstanceOf(error, 'E_FORBIDDEN', E_FORBIDDEN)) {
    return response.status(403).json({
      error: 'FORBIDDEN',
      message: 'Access denied'
    })
  }
  
  if (guards.isInstanceOf(error, 'E_INVALID_PAYLOAD_EXCEPTION', E_INVALID_PAYLOAD_EXCEPTION)) {
    return response.status(422).json({
      error: 'VALIDATION_ERROR',
      message: 'Invalid input data',
      details: error.details
    })
  }
  
  if (guards.isInstanceOf(error, 'E_RECORD_NOT_FOUND_EXCEPTION', E_RECORD_NOT_FOUND_EXCEPTION)) {
    return response.status(404).json({
      error: 'NOT_FOUND',
      message: 'Resource not found'
    })
  }
  
  // Unknown error - re-throw
  throw error
}

Comprehensive Error Handler ​

Here's an example of a comprehensive error handler that handles all Lucid Resourceful error types:

typescript
import { guards } from '@nhtio/lucid-resourceful/utils'
import { errors } from '@nhtio/lucid-resourceful'

class ResourcefulErrorHandler {
  static handle(error: unknown, response: Response) {
    // Configuration and setup errors
    if (guards.isInstanceOf(error, 'E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS', errors.E_INVALID_RESOURCEFUL_DATA_TYPE_OPTIONS)) {
      return this.handleConfigurationError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_INVALID_RESOURCEFUL_MIXIN_OPTIONS', errors.E_INVALID_RESOURCEFUL_MIXIN_OPTIONS)) {
      return this.handleConfigurationError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS', errors.E_INVALID_RESOURCEFUL_DECORATOR_OPTIONS)) {
      return this.handleConfigurationError(error, response)
    }
    
    // Validation errors
    if (guards.isInstanceOf(error, 'E_INVALID_PAYLOAD_EXCEPTION', errors.E_INVALID_PAYLOAD_EXCEPTION)) {
      return this.handleValidationError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_FORBIDDEN_PAYLOAD_EXCEPTION', errors.E_FORBIDDEN_PAYLOAD_EXCEPTION)) {
      return this.handleForbiddenPayloadError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION', errors.E_INVALID_RESOUREFUL_INDEX_REQUEST_EXCEPTION)) {
      return this.handleIndexRequestError(error, response)
    }
    
    // Access control errors
    if (guards.isInstanceOf(error, 'E_FORBIDDEN', errors.E_FORBIDDEN)) {
      return this.handleForbiddenError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_INVALID_COLUMN_ACCESS', errors.E_INVALID_COLUMN_ACCESS)) {
      return this.handleColumnAccessError(error, response)
    }
    
    // Query and database errors
    if (guards.isInstanceOf(error, 'E_RECORD_NOT_FOUND_EXCEPTION', errors.E_RECORD_NOT_FOUND_EXCEPTION)) {
      return this.handleNotFoundError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_MISSING_PRIMARY_KEY_EXCEPTION', errors.E_MISSING_PRIMARY_KEY_EXCEPTION)) {
      return this.handleMissingPrimaryKeyError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_LUCENE_SYNTAX_EXCEPTION', errors.E_LUCENE_SYNTAX_EXCEPTION)) {
      return this.handleLuceneSyntaxError(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_INVALID_LUCENE_QUERY', errors.E_INVALID_LUCENE_QUERY)) {
      return this.handleInvalidLuceneQuery(error, response)
    }
    
    if (guards.isInstanceOf(error, 'E_UNEXPECTED_COLUMN_IN_QUERY', errors.E_UNEXPECTED_COLUMN_IN_QUERY)) {
      return this.handleUnexpectedColumnError(error, response)
    }
    
    // Unknown error
    return this.handleUnknownError(error, response)
  }
  
  private static handleValidationError(error: errors.E_INVALID_PAYLOAD_EXCEPTION, response: Response) {
    return response.status(422).json({
      error: 'VALIDATION_ERROR',
      message: 'The provided data is invalid',
      details: error.details?.map(detail => ({
        field: detail.path.join('.'),
        message: detail.message,
        value: detail.context?.value
      }))
    })
  }
  
  private static handleForbiddenError(error: errors.E_FORBIDDEN, response: Response) {
    return response.status(403).json({
      error: 'FORBIDDEN',
      message: 'You do not have permission to perform this action'
    })
  }
  
  private static handleNotFoundError(error: errors.E_RECORD_NOT_FOUND_EXCEPTION, response: Response) {
    return response.status(404).json({
      error: 'NOT_FOUND',
      message: 'The requested resource was not found'
    })
  }
  
  private static handleLuceneSyntaxError(error: errors.E_LUCENE_SYNTAX_EXCEPTION, response: Response) {
    return response.status(422).json({
      error: 'QUERY_SYNTAX_ERROR',
      message: 'Invalid query syntax',
      details: {
        line: error.line,
        column: error.column,
        offset: error.offset
      }
    })
  }
  
  // ... other error handlers
}

Error Context and Debugging ​

Joi Validation Details ​

Many Lucid Resourceful errors include Joi validation details that provide structured information about validation failures:

typescript
try {
  const user = await User.$onResourcefulCreate(invalidPayload, ctx, app)
} catch (error) {
  if (error instanceof E_INVALID_PAYLOAD_EXCEPTION && error.details) {
    error.details.forEach(detail => {
      console.log(`Field: ${detail.path.join('.')}`)
      console.log(`Message: ${detail.message}`)
      console.log(`Value: ${detail.context?.value}`)
      console.log(`Label: ${detail.context?.label}`)
    })
  }
}

Error Chaining ​

Lucid Resourceful errors preserve the original error cause when wrapping lower-level errors:

typescript
try {
  // Operation that might fail
} catch (error) {
  if (error instanceof E_LUCENE_SYNTAX_EXCEPTION) {
    console.log('Resourceful error:', error.message)
    console.log('Original error:', error.cause) // Original Lucene parser error
  }
}

Development vs Production Error Handling ​

Handle errors differently based on environment:

typescript
class ErrorHandler {
  static handle(error: unknown, response: Response, isDevelopment: boolean = false) {
    if (guards.isInstanceOf(error, 'E_INVALID_PAYLOAD_EXCEPTION', E_INVALID_PAYLOAD_EXCEPTION)) {
      const responseData = {
        error: 'VALIDATION_ERROR',
        message: 'Invalid input data'
      }
      
      if (isDevelopment) {
        // Include detailed validation information in development
        responseData.details = error.details
        responseData.stack = error.stack
      }
      
      return response.status(422).json(responseData)
    }
    
    // Handle other errors similarly
  }
}

Best Practices ​

1. Use Type Guards for Reliable Error Checking ​

Always use the isInstanceOf type guard for error checking to ensure compatibility across different bundling and transformation scenarios:

typescript
// ✅ Good: Using type guard
if (guards.isInstanceOf(error, 'E_FORBIDDEN', E_FORBIDDEN)) {
  // Handle forbidden error
}

// ❌ Avoid: Direct instanceof (may not work with bundled code)
if (error instanceof E_FORBIDDEN) {
  // This might fail in some environments
}

2. Provide User-Friendly Error Messages ​

Transform technical error messages into user-friendly ones:

typescript
const ERROR_MESSAGES = {
  E_INVALID_PAYLOAD_EXCEPTION: 'Please check your input and try again',
  E_FORBIDDEN: 'You do not have permission to perform this action',
  E_RECORD_NOT_FOUND_EXCEPTION: 'The requested item was not found',
  E_LUCENE_SYNTAX_EXCEPTION: 'Your search query is invalid'
}

function getUserFriendlyMessage(error: unknown): string {
  if (error instanceof Error && error.code) {
    return ERROR_MESSAGES[error.code as keyof typeof ERROR_MESSAGES] || 'An unexpected error occurred'
  }
  return 'An unexpected error occurred'
}

3. Log Errors for Debugging ​

Always log errors with sufficient context for debugging:

typescript
import { Logger } from '@adonisjs/logger'

class ErrorLogger {
  static log(error: unknown, context: Record<string, any> = {}) {
    if (guards.isInstanceOf(error, 'E_INVALID_PAYLOAD_EXCEPTION', E_INVALID_PAYLOAD_EXCEPTION)) {
      Logger.warn('Validation error occurred', {
        error: error.message,
        details: error.details,
        context
      })
    } else if (guards.isInstanceOf(error, 'E_FORBIDDEN', E_FORBIDDEN)) {
      Logger.warn('Access denied', {
        error: error.message,
        context
      })
    } else {
      Logger.error('Unexpected error', {
        error: error.message,
        stack: error.stack,
        context
      })
    }
  }
}

4. Handle Validation Details Appropriately ​

Extract useful information from Joi validation details:

typescript
function formatValidationErrors(details: ValidationError['details'] = []) {
  return details.map(detail => {
    const field = detail.path.join('.')
    const message = detail.message.replace(/["']/g, '') // Remove quotes
    
    return {
      field,
      message,
      value: detail.context?.value,
      code: detail.type
    }
  })
}

5. Graceful Degradation ​

Handle errors gracefully to maintain application stability:

typescript
async function safeOperation<T>(
  operation: () => Promise<T>,
  fallback: T
): Promise<T> {
  try {
    return await operation()
  } catch (error) {
    // Log the error
    ErrorLogger.log(error)
    
    // Return fallback value instead of crashing
    return fallback
  }
}

// Usage
const users = await safeOperation(
  () => User.$onResourcefulIndex(query, page, perPage, fields, ctx, app),
  { records: [], total: 0, page: 1, perPage: 10 }
)

API Reference ​

For detailed API documentation of error classes, see: