Skip to content

guard

Protect async functions, catch errors and return a unified result format.

Basic Usage

typescript
import { guard } from 'radash'

const result = await guard(async () => {
  const response = await fetch('https://api.example.com/data')
  return response.json()
})

if (result.error) {
  console.log('Error:', result.error)
} else {
  console.log('Data:', result.data)
}

Syntax

typescript
function guard<T>(
  fn: () => Promise<T>
): Promise<{ data: T | null; error: Error | null }>

Parameters

  • fn (function): The async function to protect

Return Value

Returns a Promise that resolves to an object containing data and error.

Examples

Basic Error Handling

typescript
import { guard } from 'radash'

async function fetchUserData(userId: number) {
  const result = await guard(async () => {
    const response = await fetch(`/api/users/${userId}`)
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${response.statusText}`)
    }
    return response.json()
  })

  if (result.error) {
    console.error('Failed to fetch user:', result.error.message)
    return null
  }

  return result.data
}

const user = await fetchUserData(123)

Handle Network Requests

typescript
import { guard } from 'radash'

async function fetchMultipleUsers(userIds: number[]) {
  const results = await Promise.all(
    userIds.map(id => 
      guard(async () => {
        const response = await fetch(`/api/users/${id}`)
        if (!response.ok) {
          throw new Error(`Failed to fetch user ${id}`)
        }
        return response.json()
      })
    )
  )

  return results.map(result => 
    result.error ? { error: result.error.message } : { data: result.data }
  )
}

const users = await fetchMultipleUsers([1, 2, 3, 4, 5])

Database Operations

typescript
import { guard } from 'radash'

async function createUser(userData: any) {
  const result = await guard(async () => {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(userData)
    })
    
    if (!response.ok) {
      throw new Error('Failed to create user')
    }
    
    return response.json()
  })

  if (result.error) {
    console.error('User creation failed:', result.error.message)
    return { success: false, error: result.error.message }
  }

  return { success: true, user: result.data }
}

File Operations

typescript
import { guard } from 'radash'
import { readFile, writeFile } from 'fs/promises'

async function processFile(filename: string) {
  const readResult = await guard(async () => {
    const content = await readFile(filename, 'utf8')
    return JSON.parse(content)
  })

  if (readResult.error) {
    console.error('Failed to read file:', readResult.error.message)
    return null
  }

  const data = readResult.data
  data.processed = true
  data.timestamp = new Date().toISOString()

  const writeResult = await guard(async () => {
    await writeFile(filename, JSON.stringify(data, null, 2))
    return data
  })

  if (writeResult.error) {
    console.error('Failed to write file:', writeResult.error.message)
    return null
  }

  return writeResult.data
}

API Rate Limiting

typescript
import { guard } from 'radash'

async function fetchWithRetry(url: string, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const result = await guard(async () => {
      const response = await fetch(url)
      if (response.status === 429) {
        throw new Error('Rate limited')
      }
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      return response.json()
    })

    if (!result.error) {
      return result.data
    }

    if (result.error.message === 'Rate limited') {
      await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)))
      continue
    }

    throw result.error
  }

  throw new Error('Max retries exceeded')
}

Validation with Guard

typescript
import { guard } from 'radash'

async function validateAndProcess(data: any) {
  const validationResult = await guard(async () => {
    if (!data.email || !data.email.includes('@')) {
      throw new Error('Invalid email')
    }
    if (!data.age || data.age < 0) {
      throw new Error('Invalid age')
    }
    return data
  })

  if (validationResult.error) {
    return { valid: false, error: validationResult.error.message }
  }

  const processResult = await guard(async () => {
    // Process the validated data
    const processed = { ...validationResult.data, processed: true }
    return processed
  })

  if (processResult.error) {
    return { valid: false, error: processResult.error.message }
  }

  return { valid: true, data: processResult.data }
}

Timeout Protection

typescript
import { guard } from 'radash'

async function fetchWithTimeout(url: string, timeout = 5000) {
  const result = await guard(async () => {
    const controller = new AbortController()
    const timeoutId = setTimeout(() => controller.abort(), timeout)

    try {
      const response = await fetch(url, { signal: controller.signal })
      clearTimeout(timeoutId)
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`)
      }
      
      return response.json()
    } catch (error) {
      clearTimeout(timeoutId)
      if (error.name === 'AbortError') {
        throw new Error('Request timeout')
      }
      throw error
    }
  })

  if (result.error) {
    console.error('Request failed:', result.error.message)
    return null
  }

  return result.data
}

Batch Processing

typescript
import { guard } from 'radash'

async function processBatch(items: any[]) {
  const results = await Promise.all(
    items.map(item => 
      guard(async () => {
        // Simulate processing
        await new Promise(resolve => setTimeout(resolve, 100))
        
        if (Math.random() < 0.1) {
          throw new Error('Random processing error')
        }
        
        return { ...item, processed: true }
      })
    )
  )

  const successful = results.filter(r => !r.error).map(r => r.data)
  const failed = results.filter(r => r.error).map(r => r.error)

  return { successful, failed }
}

Notes

  1. Error handling: Automatically catches and wraps errors
  2. Type safety: Maintains TypeScript type safety
  3. Performance: Minimal overhead compared to try-catch
  4. Consistent format: Always returns { data, error } structure
  5. Async support: Designed specifically for async functions

Differences from Other Functions

  • guard: Protects async functions with error handling
  • try-catch: Manual error handling, more verbose
  • Promise.catch(): Only handles Promise rejections
  • guard(): Provides structured error handling

Performance

  • Time Complexity: O(1) overhead
  • Memory: Minimal additional memory usage
  • Use Cases: API calls, file operations, database queries

Released under the MIT License.