# Trywrap

Trywrap is a lightweight utility module designed to simplify error handling in asynchronous functions. By wrapping functions, it provides a cleaner, more readable way to manage errors, reducing the need for repetitive `try-catch` blocks.

## Installation

To install Trywrap, use npm:

```bash
npm install trywrap
```

## Usage

Here's a basic example of how to use Trywrap. This example demonstrates handling errors with a fallback value and a global error handler:

```javascript
const trywrap = require('trywrap');

async function testFn(value) {
    if (value < 0) throw new Error("Negative value");
    return `Success: ${value}`;
}

async function onError({ error, methodName, args }) {
    console.error(`Caught in ${methodName} with args ${JSON.stringify(args)}:`, error.message);
}

(async () => {
    const result = await trywrap(testFn, [10], { onError, fallback: "Default Value" });
    console.log(result); // Success: 10

    const failedResult = await trywrap(testFn, [-5], { onError, fallback: "Default Value" });
    console.log(failedResult); // Default Value
})();
```

## API

### `trywrap(cb, props, options)`

#### Parameters:
- **`cb`** *(Function, required)*: The async function to be executed.
- **`props`** *(Array, optional, default: `[]`)*: Arguments to pass to the function.
- **`options`** *(Object, optional)*:
  - **`onError`** *(Function)*: A callback function that is called with an object containing:
    - `error`: The error object.
    - `methodName`: The name of the function that threw the error.
    - `args`: The arguments passed to the function.
  - **`fallback`** *(any)*: A value to return if an error occurs.
  - **`rethrow`** *(boolean)*: If set to `true`, the error will be rethrown after calling `onError`.

## Example Use Cases

### 1. **Handling API Calls**
In this example, Trywrap is used to handle errors during API calls, providing a fallback value if the call fails:

```javascript
async function fetchData() {
    throw new Error("API failed");
}

const data = await trywrap(fetchData, [], {
    onError: ({ error, methodName, args }) => {
        console.error(`Caught in ${methodName} with args ${JSON.stringify(args)}:`, error.message);
    },
    fallback: []
});

console.log(data); // []
```

### 2. **Handling Database Queries**
Trywrap can also be used to manage errors in database queries, ensuring a fallback value is returned if an error occurs:

```javascript
async function getUser(id) {
    if (id !== 1) throw new Error("User not found");
    return { id, name: "John Doe" };
}

const user = await trywrap(getUser, [2], {
    onError: ({ error, methodName, args }) => {
        console.error(`Caught in ${methodName} with args ${JSON.stringify(args)}:`, error.message);
    },
    fallback: null
});

console.log(user); // null
```

### 3. **Rethrowing Errors**
This example illustrates how to rethrow errors after handling them, allowing for further error processing:

```javascript
async function riskyOperation() {
    throw new Error("Something went wrong");
}

async function onError({ error, methodName, args }) {   
    console.error(`Caught in ${methodName} with args ${JSON.stringify(args)}:`, error.message);
}

try {
    await trywrap(riskyOperation, [], { onError, rethrow: true });
} catch (err) {
    console.error("Error was rethrown:", err.message);
}
```

## Traditional Nested Try-Catch vs. Trywrap

### Traditional Nested Try-Catch

Traditionally, handling errors with nested `try-catch` blocks can become cumbersome and hard to maintain:

```javascript
async function outerFunction() {
    try {
        // #action-1
        try {
            await innerFunction();
        } catch (innerError) {
            console.error("Inner error caught:", innerError.message);
        }
        // #action-2
        try {
            await innerFunction();
        } catch (innerError) {
            console.error("Inner error caught:", innerError.message);
        }
        // #action-3
        await actionFunction();
        // #action-4
        await innerFunction();
    } catch (outerError) {
        console.error("Outer error caught:", outerError.message);
        try {
            await recoveryFunction();
        } catch (recoveryError) {
            console.error("Recovery error caught:", recoveryError.message);
        }
    }
}

async function actionFunction() {
    return "Action function";
}

async function innerFunction() {
    throw new Error("Inner function error");
}

async function recoveryFunction() {
    throw new Error("Recovery function error");
}

outerFunction();
```

### Using Trywrap

With Trywrap, the same logic is simplified, reducing the need for nested `try-catch` blocks and centralizing error handling:

```javascript
async function outerFunction() {
    // #action-1
    await trywrap(innerFunction, [], { onError, fallback: "Fallback for action 1" });
    // #action-2
    await trywrap(innerFunction, []);
    // #action-3
    await trywrap(actionFunction, [], { onError, fallback: "Fallback for action 3" });
    // #action-4
    await trywrap(innerFunction, [], { onError, fallback: "Fallback for action 4" });
}

async function onError({ error, methodName }) {
    console.error("Error caught in", methodName, error.message);
}

async function actionFunction() {
    return "Action function";
}

async function innerFunction() {
    throw new Error("Inner function error");
}

async function recoveryFunction() {
    throw new Error("Recovery function error");
}

outerFunction();
```

## Why Use Trywrap?

✅ **Cleaner Code:** No more repetitive `try-catch` blocks.  
✅ **Flexible:** Customize error handling with callbacks and fallbacks.  
✅ **Lightweight:** Minimal overhead with just one function.  
✅ **Works with Any Async Function:** Use it with API calls, database queries, file operations, etc.  

## License

MIT

