Skip to content

delt4d/Node-Result

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Result & AsyncResult Utility

This library provides two utility classes to manage success/failure outcomes in synchronous and asynchronous operations in a clean, predictable, and type-safe manner:

  • Result<T, E> for synchronous operations
  • AsyncResult<T, E> for asynchronous operations (Promise-wrapped)

✨ Features

  • Explicit success and error handling
  • Pattern-matching-like API (isOk(), isFail())
  • Safe transformations with map()
  • Optional throwing or fallback value with orElseThrow(), orElse()
  • Asynchronous equivalents via AsyncResult

πŸ§ͺ Examples

In a service async method call

// userService.ts
import db from './db';
import { User } from './types';

export async function getUserById(id: string): Promise<User> {
  const user = await db.user.findUnique({ where: { id } });
  if (!user) throw new Error("User not found");
  return user;
}

// userController.ts
import { getUserById } from './userService';
import { AsyncResult } from './result';

async function handleGetUserRequest(req, res) {
  const getUserByIdResult = new AsyncResult(getUserById(req.params.id));
  const isOk = await getUserByIdResult.isOkAsync();

  if (isOk) {
    res.status(200).json(userResult.value);
    return;
  }

  res.status(404).json({ error: userResult.value.message });
}

In a validation function using a schema and data

// validate.ts
import { Result } from './result';
import { z, ZodSchema } from 'zod';

export function validateSchema<T>(schema: ZodSchema<T>, data: unknown): Result<T, Error> {
  try {
    const parsed = schema.parse(data);
    return Result.success(parsed);
  } catch (err) {
    return Result.error(err instanceof Error ? err : new Error("Validation failed"));
  }
}

// usage
const userSchema = z.object({
  name: z.string().min(1),
  age: z.number().int().positive()
});

const input = { name: "", age: -2 };

const validation = validateSchema(userSchema, input);

if (validation.isFail()) {
  console.error("Validation failed:", validation.value.message);
} else {
  console.log("Valid data:", validation.value);
}

In an async utility function that processes a result pipeline with AsyncResult, performing validation, mapping, and fallbacks:

// processPayment.ts
import { AsyncResult, Result } from './result';

async function chargeCard(amount: number): Promise<string> {
  if (amount <= 0) throw new Error("Invalid amount");
  // Simulate async payment
  return `Payment of $${amount} processed`;
}

export async function processPayment(amount: number): Promise<Result<string, Error>> {
  const result = new AsyncResult(chargeCard(amount))
    .validate((res) => res.includes("processed"), new Error("Payment failed"))
    .map((res) => res.toUpperCase());

  const finalMessage = await result.orElseAsync("DEFAULT PAYMENT MESSAGE");

  // Return a resolved Result
  return Result.success(finalMessage);
}

// usage
const result = await processPayment(100);
if (result.isOk()) {
  console.log("Success:", result.value);
} else {
  console.error("Failure:", result.value.message);
}

πŸ“˜ Usage

Synchronous: Result<T, E>

βœ… Creating a success result

const result = Result.success("Data loaded!");
if (result.isOk()) {
  console.log(result.value); // "Data loaded!"
}

❌ Creating an error result

const errorResult = Result.error(new Error("Failed to load"));
if (errorResult.isFail()) {
  console.error(errorResult.value.message); // "Failed to load"
}

βœ… orElseThrow - value or throw

const data = result.orElseThrow(); // Returns the value
const error = errorResult.orElseThrow(); // Throws the error

With custom transformation:

errorResult.orElseThrow((err) => `Something went wrong: ${err.message}`);
// Throws: Error("Something went wrong: Failed to load")

βœ… orElse - value or fallback

const fallback = errorResult.orElse("default");
console.log(fallback); // "default"

βœ… map - transform successful value

const upper = result.map((str) => str.toUpperCase());
console.log(upper.value); // "DATA LOADED!"

If transformation throws:

const risky = result.map(() => {
  throw new Error("Boom!");
});
console.log(risky.isFail()); // true

βœ… validate - enforce conditions

const valid = result.validate(val => val.length > 0, new Error("Empty string"));

Asynchronous: AsyncResult<T, E>

Wrap any promise into an AsyncResult:

const promise = fetch("/api/data").then(res => res.json());
const asyncRes = new AsyncResult(promise);

βœ… Check status

const ok = await asyncRes.isOkAsync();
const fail = await asyncRes.isFailAsync();

βœ… Get resolved value, error or result

const result = await asyncRes.getResultAsync(); // T | E
const value = await asyncRes.getValueAsync(); // T | null
const error = await asyncRes.getErrorAsync(); // E | null

βœ… orElseAsync

const fallbackData = await asyncRes.orElseAsync({ default: true });

βœ… orElseThrowAsync

const data = await asyncRes.orElseThrowAsync();

With transformation:

await asyncRes.orElseThrowAsync((err) => new Error("Custom: " + err.message));

βœ… waitAsync

await asyncRes.waitAsync(); // Does not throw on failure

βœ… map async value

const transformed = asyncRes.map(data => data.items.length);

βœ… validate async result

const validated = asyncRes.validate(data => data.items.length > 0, new Error("No items found"));

βœ… resolveAsync to Result

const syncResult = await asyncRes.resolveAsync();
if (syncResult.isOk()) {
  console.log(syncResult.value);
}

About

Library that provides classes to manage results in synchronous and asynchronous operations.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors