Skip to content

πŸ›‘ Typescript promise result guarding library

License

Notifications You must be signed in to change notification settings

meistrari/resguard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

24 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ›‘οΈ resguard

npm version npm downloads Codecov

resguard is a tiny utility that wraps promises and returns an object or tuple with data and error properties. It's useful for handling errors in async functions without having to use try/catch blocks.

highlights

  • πŸ›‘ Wraps promises and returns an object or tuple with data and error properties
  • 🎯 TypeScript support with type checking
  • πŸ› οΈ Custom error handling support
  • ⚑ Minimal dependencies and small bundle size

usage

npm install resguard
import { resguard } from 'resguard'

async function fetchData() {
    const client = new APIClient()

    const { data, error } = await resguard(client.getItems())
    if (error) 
        handle(error)
    
    const updated = await resguard(client.updateItems(data))
    if (updated.error) 
        handle(updated.error)

    return updated.data
}

Both the data and error properties of the result are correctly typed

import { resguard } from 'resguard'

const result = resguard(() => {
    if (Math.random() > 0.5) 
        return true
    else 
        throw new Error('Something went wrong')
})

if (result.error) 
    handle(result.error)

resguard can also be used with functions. When they are sync, the result also is!

import { resguard } from 'resguard'

const result = await resguard(async () => {
    const client = new APIClient()
    const items = await client.getItems()
    return items.map(item => item.id)
})

if (result.error) 
    handle(result.error)

resguard can also be used with async functions.

import { resguardFn } from 'resguard'

const safeJSONParse = resguardFn(JSON.parse)

let result = safeJSONParse('{ "test": 1 }')
console.log(result.data) // { test: 1 }

result = safeJSONParse('{ missing the other one')
console.log(result.error) // SyntaxError: Unexpected character 'm' (1:2)

resguardFn is a wrapper around resguard that takes a function as an argument and returns a function that can be called with the same arguments, but guarded.

❌ depressing

βœ… awesome

let result

try {
    result = await client.getItems()
} catch (error) {
    handle(error)
}
const result = await resguard(client.getItems())
if (result.error) 
    handle(result.error)
let result

try {
    result = await client.longRequest()
} catch (e: any) {
    const error: ClientError = e
    if (error.code === 'TIMEOUT')
      handleTimeout()
}
const result = await resguard(client.longRequest(), ClientError)
if (result.error) {
    if (error.code === 'TIMEOUT')
      handleTimeout()
}
let result
try {
  result = JSON.parse(data)
} catch (e: any) {
  const error: SyntaxError = e
  handle(error)
}
const result = resguard(() => JSON.parse(data), SyntaxError)
if (result.error) 
    handle(result.error)
let data: { test: number }

try {
  data = JSON.parse('{ test: 1 }')
} catch (e: any) {
  const error: SyntaxError = e
  handle(error)
}

console.log(data.test)
const { data, error } = resguard<{ test: number}>(
    () => JSON.parse('{ test: 1 }'), 
    SyntaxError
)

if (error) 
    handle(error)

console.log(data.test)
async function complexFunction() {
  let items
  try {
    items = await client.getItems()
  } catch (e: any) {
    const error: ClientError = e
    handle(error)
  }

  let updated
  try {
    updated = await client.updateItems(items)
  } catch (e: any) {
    const error: ClientError = e
    handle(error)
  }

  return updated
}
async function complexFunction() {
  const items = await resguard(client.getItems(), ClientError)
  if (items.error) 
    handle(items.error)

  const updatedItems = await resguard(client.updateItems(items), ClientError)
  if (updatedItems.error) 
    handle(updatedItems.error)

  return updatedItems.data
}

using tuples

resguard can also return a tuple with data and error values:

import { resguard } from 'resguard';

async function fetchData() {
  const service = new DataService();
  const [[data, error]] = await resguard(service.getItems());

  if (error) {
    console.error("Error:", error);
    return;
  }

  return data;
}

custom error handling

resguard supports custom error handling by allowing you to override the error type:

import { resguard } from 'resguard';

class CustomError extends Error {}

async function fetchData() {
  const service = new DataService();
  const [[data, error]] = await resguard(() => throw new CustomError('damn!'), CustomError);

  if (error) {
    console.error("Custom Error:", error);
    console.log(error instanceof CustomError) // true
    return;
  }

  return data;
}