Skip to content

Utilities

Lucid Resourceful provides a comprehensive set of utilities for data type casting, preparation, consumption, and type checking through the @nhtio/lucid-resourceful/utils module. These utilities are essential for ensuring data integrity and type safety when working with database operations and API interactions.

Overview

The utilities module is organized into four main namespaces:

  • casters - Type casting utilities for converting unknown values to specific types
  • preparers - Data preparation utilities for database storage
  • consumers - Data consumption utilities for retrieving values from database results
  • guards - Type guard utilities for runtime type checking
typescript
import { casters, preparers, consumers, guards } from '@nhtio/lucid-resourceful/utils'

// Cast unknown values to specific types
const stringValue = casters.castValueAsString(unknownValue)

// Prepare values for database storage
const preparedValue = preparers.prepareString('name', inputValue, false)

// Consume values from database results
const consumedValue = consumers.consumeString('name', dbValue, true)

// Check types at runtime
if (guards.isObject(value)) {
  // value is guaranteed to be an object
}

Casters

The casters namespace provides utilities for converting unknown values to specific data types with comprehensive error handling. These functions are primarily used internally by Lucid Resourceful but are available for custom data processing needs.

Available Casters

All caster functions throw E_UNCASTABLE errors when values cannot be cast to the target type.

String Casting

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

// Cast values to string
const result = casters.castValueAsString('hello')     // 'hello'
const result2 = casters.castValueAsString(123)       // '123'

// Throws E_UNCASTABLE for incompatible types
try {
  casters.castValueAsString({ key: 'value' })
} catch (error) {
  // E_UNCASTABLE: Cannot cast value to String
}

Numeric Casting

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

// Cast to number
const num = casters.castValueAsNumber('123.45')      // 123.45
const num2 = casters.castValueAsNumber(456)          // 456

// Cast to integer (truncated)
const int = casters.castValueAsInteger(123.89)       // 123
const int2 = casters.castValueAsInteger('456.78')    // 456

// Cast to bigint
const big = casters.castValueAsBigint('123')         // 123n
const big2 = casters.castValueAsBigint(456)          // 456n

// Cast to unsigned integer (32-bit)
const uint = casters.castValueAsUnsignedInt(-5)      // 4294967291 (wrapped)
const uint2 = casters.castValueAsUnsignedInt('123')  // 123

Date and Time Casting

typescript
import { casters } from '@nhtio/lucid-resourceful/utils'
import { DateTime } from 'luxon'

// Cast to Luxon DateTime with enhanced parsing
const date1 = casters.castValueAsDate('2023-12-25')               // From SQL format
const date2 = casters.castValueAsDate('2023-12-25T10:30:00Z')     // From ISO format
const date3 = casters.castValueAsDate(1703505000)                 // From Unix timestamp
const date4 = casters.castValueAsDate(new Date())                 // From JS Date
const date5 = casters.castValueAsDate(DateTime.now())             // From Luxon DateTime

// castValueAsDateTime is an alias for castValueAsDate with enhanced error messages
const dateTime = casters.castValueAsDateTime('2023-12-25T10:30:00Z')

Boolean Casting

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

// Cast to boolean with flexible string parsing
const bool1 = casters.castValueAsBoolean(true)       // true
const bool2 = casters.castValueAsBoolean('true')     // true
const bool3 = casters.castValueAsBoolean('1')        // true
const bool4 = casters.castValueAsBoolean('false')    // false
const bool5 = casters.castValueAsBoolean('0')        // false
const bool6 = casters.castValueAsBoolean(1)          // true
const bool7 = casters.castValueAsBoolean(0)          // false

// Case insensitive string parsing
const bool8 = casters.castValueAsBoolean('TRUE')     // true
const bool9 = casters.castValueAsBoolean('False')    // false

Binary Data Casting

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

// Cast to binary data (Uint8Array or Buffer)
const buffer = Buffer.from('hello')
const uint8Array = new Uint8Array([72, 101, 108, 108, 111])

const binary1 = casters.castValueAsBinary(buffer)     // Buffer
const binary2 = casters.castValueAsBinary(uint8Array) // Uint8Array

// Throws E_UNCASTABLE for non-binary data
try {
  casters.castValueAsBinary('not binary')
} catch (error) {
  // E_UNCASTABLE: Cannot cast value to Binary
}

Object and Array Casting

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

// Cast to plain object
const obj = casters.castValueAsObject({ name: 'John', age: 30 })
// Result: { name: 'John', age: 30 }

// Cast to array
const arr = casters.castValueAsArray([1, 2, 3, 'hello'])
// Result: [1, 2, 3, 'hello']

// Throws E_UNCASTABLE for incompatible types
try {
  casters.castValueAsObject('not an object')
} catch (error) {
  // E_UNCASTABLE: Cannot cast value to Object
}

Complete Caster Reference

FunctionInput TypesOutput TypeDescription
castValueAsStringstring, numberstringConverts values to string representation
castValueAsDatestring, number, Date, DateTimeDateTimeConverts values to Luxon DateTime with enhanced parsing
castValueAsDateTimestring, number, Date, DateTimeDateTimeAlias for castValueAsDate with enhanced error handling
castValueAsBinaryUint8Array, BufferUint8ArrayConverts binary data to Uint8Array
castValueAsNumbernumber, stringnumberConverts values to numeric type
castValueAsIntegernumber, stringnumberConverts values to integer (truncated)
castValueAsBigintbigint, number, stringbigintConverts values to bigint type
castValueAsUnsignedIntnumber, bigint, stringnumberConverts values to 32-bit unsigned integer
castValueAsBooleanboolean, string, numberbooleanConverts values to boolean with flexible parsing
castValueAsObjectobjectLucidPlainObjectConverts values to plain object
castValueAsArrayArray<unknown>Array<unknown>Converts values to array type

Preparers

The preparers namespace provides utilities for preparing values before they are stored in the database. These functions include nullable support and comprehensive error handling for data validation.

Nullable Support

All preparer functions support nullable fields through overloaded signatures:

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

// Non-nullable field - returns string, throws on invalid input
const required: string = preparers.prepareString('name', 'John', false)

// Nullable field - returns string | null, returns null for null input
const optional: string | null = preparers.prepareString('nickname', null, true)

Preparation Error Handling

Preparer functions throw E_INVALID_PREPARED_VALUE errors when values cannot be prepared for database storage, providing the field name and expected type for debugging.

String Preparation

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

// Prepare string values with nullable support
const name = preparers.prepareString('name', 'John Doe', false)        // 'John Doe'
const nickname = preparers.prepareString('nickname', null, true)       // null
const title = preparers.prepareString('title', 123, false)             // '123' (converted)

// Error handling for invalid values
try {
  preparers.prepareString('name', { invalid: 'object' }, false)
} catch (error) {
  // E_INVALID_PREPARED_VALUE: Cannot prepare field 'name' as String
}

Numeric Preparation

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

// Prepare numeric values
const age = preparers.prepareNumber('age', '25', false)                 // 25
const score = preparers.prepareNumber('score', null, true)              // null
const count = preparers.prepareInteger('count', 25.7, false)            // 25 (truncated)
const id = preparers.prepareBigint('id', '12345', false)                // 12345n
const flags = preparers.prepareUnsignedint('flags', -1, false)          // 4294967295 (wrapped)

Date and Time Preparation

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

// Prepare date/time values with enhanced parsing
const created = preparers.prepareDate('createdAt', '2023-12-25', false)
const updated = preparers.prepareDateTime('updatedAt', new Date(), false)
const deleted = preparers.prepareDate('deletedAt', null, true)          // null

// Supports multiple input formats
const date1 = preparers.prepareDate('date', '2023-12-25T10:30:00Z', false)  // ISO format
const date2 = preparers.prepareDate('date', '2023-12-25 10:30:00', false)   // SQL format
const date3 = preparers.prepareDate('date', 1703505000, false)              // Unix timestamp

Boolean and Binary Preparation

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

// Prepare boolean values with flexible parsing
const active = preparers.prepareBoolean('active', 'true', false)        // true
const verified = preparers.prepareBoolean('verified', 1, false)         // true
const deleted = preparers.prepareBoolean('deleted', null, true)         // null

// Prepare binary data
const avatar = preparers.prepareBinary('avatar', buffer, false)
const signature = preparers.prepareBinary('signature', null, true)      // null

Object and Array Preparation

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

// Prepare complex data types
const metadata = preparers.prepareObject('metadata', { version: 1 }, false)
const tags = preparers.prepareArray('tags', ['tag1', 'tag2'], false)
const config = preparers.prepareObject('config', null, true)            // null

Complete Preparer Reference

FunctionInput TypesNullable SupportDescription
prepareStringunknownPrepares string values for database storage
prepareDateunknownPrepares date values with enhanced parsing
prepareDateTimeunknownPrepares datetime values with enhanced parsing
prepareBinaryunknownPrepares binary data for storage
prepareNumberunknownPrepares numeric values for storage
prepareIntegerunknownPrepares integer values (truncated)
prepareBigintunknownPrepares bigint values for storage
prepareUnsignedintunknownPrepares unsigned integer values
prepareBooleanunknownPrepares boolean values with flexible parsing
prepareObjectunknownPrepares object values for storage
prepareArrayunknownPrepares array values for storage

Consumers

The consumers namespace provides utilities for safely consuming values from database results. These functions ensure that data retrieved from the database is properly typed and validated before use in your application.

Database Result Processing

Consumer functions are essential when working with raw database results where type safety is not guaranteed:

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

// Process database results safely
async function processUserRecord(dbResult: any) {
  const id = consumers.consumeNumber('id', dbResult.id, false)
  const name = consumers.consumeString('name', dbResult.name, false)
  const email = consumers.consumeString('email', dbResult.email, true)  // nullable
  const createdAt = consumers.consumeDate('created_at', dbResult.created_at, false)
  
  return { id, name, email, createdAt }
}

Consumption Error Handling

Consumer functions throw E_INVALID_CONSUMED_VALUE errors when database values cannot be consumed as expected types:

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

try {
  const age = consumers.consumeNumber('age', 'not a number', false)
} catch (error) {
  // E_INVALID_CONSUMED_VALUE: Cannot consume field 'age' as Number
  console.log(`Failed to process field: ${error.field}`)
  console.log(`Expected type: ${error.expectedType}`)
}

String Consumption

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

// Consume string values from database results
const title = consumers.consumeString('title', dbResult.title, false)
const description = consumers.consumeString('description', dbResult.description, true)

// Handles type conversion for compatible types
const id = consumers.consumeString('id', 123, false)  // '123'

Numeric Consumption

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

// Consume various numeric types
const price = consumers.consumeNumber('price', dbResult.price, false)
const quantity = consumers.consumeInteger('quantity', dbResult.quantity, false)
const userId = consumers.consumeBigint('user_id', dbResult.user_id, false)
const flags = consumers.consumeUnsignedint('flags', dbResult.flags, true)

// Handles string-to-number conversion
const count = consumers.consumeNumber('count', '42', false)  // 42

Date and Time Consumption

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

// Consume date/time values with enhanced parsing
const created = consumers.consumeDate('created_at', dbResult.created_at, false)
const updated = consumers.consumeDateTime('updated_at', dbResult.updated_at, true)

// Supports multiple database date formats
const date1 = consumers.consumeDate('date', '2023-12-25 10:30:00', false)     // SQL format
const date2 = consumers.consumeDate('date', '2023-12-25T10:30:00Z', false)    // ISO format
const date3 = consumers.consumeDate('date', 1703505000, false)                // Unix timestamp

Boolean and Binary Consumption

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

// Consume boolean values with flexible parsing
const active = consumers.consumeBoolean('is_active', dbResult.is_active, false)
const verified = consumers.consumeBoolean('verified', '1', false)  // true

// Consume binary data
const avatar = consumers.consumeBinary('avatar', dbResult.avatar, true)
const thumbnail = consumers.consumeBinary('thumbnail', buffer, false)

Complex Data Consumption

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

// Consume JSON/object fields
const metadata = consumers.consumeObject('metadata', dbResult.metadata, true)
const settings = consumers.consumeObject('settings', '{"theme": "dark"}', false)

// Consume array fields
const tags = consumers.consumeArray('tags', dbResult.tags, false)
const permissions = consumers.consumeArray('permissions', JSON.parse(dbResult.permissions), true)

Complete Consumer Reference

FunctionReturn TypeNullable SupportDescription
consumeStringstring | nullSafely consumes string values from database
consumeDateDateTime | nullConsumes date values with enhanced parsing
consumeDateTimeDateTime | nullConsumes datetime values with enhanced parsing
consumeBinaryLucidBinaryValue | nullConsumes binary data from database
consumeNumbernumber | nullConsumes numeric values from database
consumeIntegernumber | nullConsumes integer values (truncated)
consumeBigintbigint | nullConsumes bigint values from database
consumeUnsignedintnumber | nullConsumes unsigned integer values
consumeBooleanboolean | nullConsumes boolean values with flexible parsing
consumeObjectLucidPlainObject | nullConsumes object/JSON values from database
consumeArrayArray<unknown> | nullConsumes array/JSON values from database

Guards

The guards namespace provides type guard utilities for runtime type checking. These functions help ensure type safety when working with unknown values and are extensively used throughout Lucid Resourceful.

Basic Type Guards

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

// Check for basic types
if (guards.isObject(value)) {
  // TypeScript knows value is { [key: string]: unknown }
  console.log(value.someProperty)
}

if (guards.isArray(value)) {
  // TypeScript knows value is unknown[]
  console.log(value.length)
}

Luxon DateTime Detection

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

// Safely check for Luxon DateTime instances
if (guards.isLuxonDateTime(value)) {
  // TypeScript knows value is DateTime
  console.log(value.toISO())
  console.log(value.isValid)
}

// Works with actual DateTime instances
const now = DateTime.now()
console.log(guards.isLuxonDateTime(now))  // true

Binary Data Detection

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

// Check for binary data (Uint8Array or Buffer)
if (guards.isLucidBinaryValue(value)) {
  // TypeScript knows value is Uint8Array or Buffer
  console.log(value.byteLength)
}

// Works with both Uint8Array and Buffer
const uint8Array = new Uint8Array([1, 2, 3])
const buffer = Buffer.from('hello')

console.log(guards.isLucidBinaryValue(uint8Array))  // true
console.log(guards.isLucidBinaryValue(buffer))      // true
console.log(guards.isLucidBinaryValue('string'))    // false

Instance Type Checking

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

// Check if value is instance of specific class
class CustomClass {
  customMethod() {
    return 'custom'
  }
}
const instance = new CustomClass()

// Using constructor with type parameter
if (guards.isInstanceOf<CustomClass>(instance, 'CustomClass', CustomClass)) {
  // TypeScript knows instance is CustomClass
  console.log(instance.customMethod())
}

// Using class name only with generic type
if (guards.isInstanceOf<CustomClass>(instance, 'CustomClass')) {
  // More flexible checking without constructor reference
  // TypeScript knows instance is CustomClass
  console.log(instance.customMethod())
}

// Practical example with unknown value
function processValue(value: unknown) {
  if (guards.isInstanceOf<Date>(value, 'Date')) {
    // TypeScript knows value is Date
    console.log(value.getTime())
    console.log(value.toISOString())
  }
}

ResourcefulModel Detection

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

// Check if value is a ResourcefulModel
if (guards.isResourcefulModel(value)) {
  // TypeScript knows value has resourceful properties
  console.log(value.$resourcefulName)
  console.log(value.$resourcefulColumns)
  console.log(value.$resourcefulRelationships)
}

Practical Usage Examples

Safe Property Access

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

function processData(data: unknown) {
  if (guards.isObject(data)) {
    // Safe to access object properties
    const name = guards.isString(data.name) ? data.name : 'Unknown'
    const age = guards.isNumber(data.age) ? data.age : 0
    
    return { name, age }
  }
  
  throw new Error('Expected object data')
}

Database Result Validation

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

function validateUserRecord(record: unknown) {
  if (!guards.isObject(record)) {
    throw new Error('Invalid user record format')
  }
  
  const errors: string[] = []
  
  if (!guards.isNumber(record.id)) {
    errors.push('Missing or invalid user ID')
  }
  
  if (!guards.isString(record.email)) {
    errors.push('Missing or invalid email')
  }
  
  if (record.created_at && !guards.isLuxonDateTime(record.created_at)) {
    errors.push('Invalid creation date format')
  }
  
  if (errors.length > 0) {
    throw new Error(`Validation failed: ${errors.join(', ')}`)
  }
  
  return record
}

Type-Safe Data Processing

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

function processApiResponse(response: unknown) {
  if (!guards.isObject(response)) {
    throw new Error('Invalid API response')
  }
  
  const result: any = {}
  
  // Process string fields
  if (guards.isString(response.name)) {
    result.name = response.name
  }
  
  // Process array fields
  if (guards.isArray(response.tags)) {
    result.tags = response.tags.filter(guards.isString)
  }
  
  // Process nested objects
  if (guards.isObject(response.metadata)) {
    result.metadata = response.metadata
  }
  
  return result
}

Complete Guards Reference

FunctionType GuardDescription
isLuxonDateTimeDateTimeChecks if value is a Luxon DateTime instance
isLucidBinaryValueLucidBinaryValueChecks if value is binary data (Uint8Array or Buffer)
isObject{ [key: string]: unknown }Checks if value is a plain object (not null, not array)
isArrayunknown[]Checks if value is an array
isInstanceOfTChecks if value is instance of specific class
isResourcefulModelResourcefulModelChecks if value is a ResourcefulModel instance

Best Practices

Utility Error Handling

Always handle potential casting and preparation errors appropriately:

typescript
import { casters, preparers } from '@nhtio/lucid-resourceful/utils'
import { errors } from '@nhtio/lucid-resourceful'

try {
  const value = casters.castValueAsNumber(userInput)
  const prepared = preparers.prepareNumber('score', value, false)
} catch (error) {
  if (errors.isInstanceOf.E_UNCASTABLE(error)) {
    console.log(`Cannot cast to ${error.expectedTypes.join(' or ')}`)
  } else if (errors.isInstanceOf.E_INVALID_PREPARED_VALUE(error)) {
    console.log(`Invalid ${error.expectedType} for field '${error.field}'`)
  }
}

Type Safety

Use type guards to ensure type safety in dynamic code:

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

function processApiData(data: unknown) {
  if (!guards.isObject(data)) {
    throw new Error('Expected object data')
  }
  
  // Now safely access properties with proper type checking
  const id = guards.isNumber(data.id) 
    ? data.id 
    : consumers.consumeNumber('id', data.id, false)
}

Performance Considerations

  • Caching: Consider caching type-checked results for frequently accessed data
  • Early Validation: Use guards early in data processing pipelines to fail fast
  • Batch Processing: Group similar type operations together for better performance

Integration with Resourceful

These utilities integrate seamlessly with Lucid Resourceful's data type system:

typescript
import { ResourcefulStringType } from '@nhtio/lucid-resourceful/definitions'
import { preparers } from '@nhtio/lucid-resourceful/utils'

// Custom data processing using preparers
function customStringHandler(fieldName: string, value: unknown, nullable: boolean) {
  const prepared = preparers.prepareString(fieldName, value, nullable)
  
  // Additional custom processing
  if (prepared && prepared.length > 255) {
    throw new Error(`Field '${fieldName}' exceeds maximum length`)
  }
  
  return prepared
}
  • Data Type Definitions - Learn about resourceful data types that use these utilities
  • Decorators - Understanding how decorators leverage these utilities internally
  • Error Handling - Complete reference for utility-related errors
  • Validation - How validation integrates with utility functions

API Reference

For complete type definitions and method signatures, see: