Examples

Real-world examples and patterns using go-errors

Examples

This page provides real-world examples of using go-errors in various scenarios. Each example demonstrates best practices and common patterns.

Basic Examples

Simple Error Handling

import { goSync } from 'go-errors';

function divide(a: number, b: number): number {
  if (b === 0) throw new Error("Division by zero");
  return a / b;
}

// Using go-errors
function safeDivide(a: number, b: number) {
  let [result, err] = goSync(() => divide(a, b));
  if (err) {
    console.log("Division failed:", err.message);
    return [null, err] as const;
  }
  return [result, null] as const;
}

// Usage
let [result1, err] = safeDivide(10, 2);
console.log(result1); // 5

let [result2, err] = safeDivide(10, 0);
console.log(err?.message); // "Division by zero"

API Calls

import { goFetch } from 'go-errors';

interface User {
  id: number;
  firstName: string;
  lastName: string;
  fullName: string;
}

async function fetchUser(id: string) {
  let [user, err] = await goFetch<User>(`/api/users/${id}`, {
    responseTransformer: (data) => ({
      ...data,
      fullName: `${data.firstName} ${data.lastName}`
    })
  });
  
  if (err) {
    return [null, err] as const;
  }
  
  return [user, null] as const;
}

// Usage
async function displayUser(id: string) {
  let [user, err] = await fetchUser(id);
  if (err) {
    console.error("Failed to fetch user:", err.message);
    return;
  }
  console.log("User:", user.fullName);
}

Advanced Examples

Custom Error Types

import { goSync } from 'go-errors';

// Define custom error types
class ValidationError extends Error {
  constructor(
    message: string,
    public field: string,
    public value: unknown
  ) {
    super(message);
    this.name = 'ValidationError';
  }
}

class DatabaseError extends Error {
  constructor(
    message: string,
    public details?: unknown
  ) {
    super(message);
    this.name = 'DatabaseError';
  }
}

// Function that might throw different types of errors
function validateAndSaveUser(user: unknown) {
  // Validation
  let [validUser, err] = goSync<User, ValidationError>(() => {
    if (typeof user !== 'object' || !user) {
      throw new ValidationError(
        'Invalid user object',
        'user',
        user
      );
    }
    return user as User;
  });

  if (err) {
    return [null, err] as const;
  }

  // Database operation
  let [savedUser, err] = goSync<User, DatabaseError>(() => {
    // Simulate database operation
    if (Math.random() < 0.5) {
      throw new DatabaseError(
        'Database connection failed',
        { timestamp: new Date() }
      );
    }
    return validUser;
  });

  if (err) {
    return [null, err] as const;
  }

  return [savedUser, null] as const;
}

// Usage with type checking
let [user, err] = validateAndSaveUser({});
if (err) {
  if (err instanceof ValidationError) {
    console.error(
      "Validation failed:",
      err.message,
      `Field: ${err.field}`,
      `Value: ${err.value}`
    );
  } else if (err instanceof DatabaseError) {
    console.error(
      "Database error:",
      err.message,
      err.details
    );
  }
}

Chaining Operations

import { goFetch } from 'go-errors';

interface User {
  id: number;
  firstName: string;
  lastName: string;
  fullName: string;
}

interface Post {
  id: number;
  userId: number;
  title: string;
}

async function fetchUserWithPosts(userId: string) {
  // Fetch user
  let [user, err] = await goFetch<User>(`/api/users/${userId}`, {
    responseTransformer: (data) => ({
      ...data,
      fullName: `${data.firstName} ${data.lastName}`
    })
  });
  if (err) return [null, err] as const;

  // Fetch user's posts
  let [posts, err] = await goFetch<Post[]>(`/api/users/${userId}/posts`);
  if (err) return [null, err] as const;

  // Return combined data
  return [{
    user,
    posts,
    timestamp: new Date()
  }, null] as const;
}

// Usage with destructuring
async function displayUserProfile(userId: string) {
  let [data, err] = await fetchUserWithPosts(userId);
  if (err) {
    console.error("Failed to fetch profile:", err.message);
    return;
  }

  const { user, posts } = data;
  console.log(`User: ${user.fullName}`);
  console.log(`Posts: ${posts.length}`);
}

File Operations

import { goSync } from 'go-errors';
import { promises as fs } from 'fs';

async function saveConfig(config: object, path: string) {
  // Convert to JSON
  let [jsonString, err] = goSync(() => 
    JSON.stringify(config, null, 2)
  );
  if (err) {
    return [null, err] as const;
  }

  // Write to file
  let [_, err] = await goSync(() =>
    fs.writeFile(path, jsonString, 'utf-8')
  );
  if (err) {
    return [null, err] as const;
  }

  return [true, null] as const;
}

// Usage
async function updateConfig() {
  const config = {
    api: {
      url: 'https://api.example.com',
      timeout: 5000
    }
  };

  let [success, err] = await saveConfig(config, './config.json');
  if (err) {
    console.error("Failed to save config:", err.message);
    return;
  }
  console.log("Config saved successfully");
}

Retry Pattern

import { goFetch } from 'go-errors';

async function withRetry<T, E = Error>(
  operation: () => Promise<T>,
  maxRetries: number = 3,
  delay: number = 1000
): Promise<Result<T, E>> {
  let lastError: E | null = null;

  for (let i = 0; i < maxRetries; i++) {
    let [result, err] = await goFetch<T, E>(operation);
    if (!err) {
      return [result, null] as const;
    }
    
    lastError = err;
    if (i < maxRetries - 1) {
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  return [null, lastError] as const;
}

// Usage
async function fetchWithRetry(url: string) {
  let [data, err] = await withRetry(
    () => goFetch(url)
  );

  if (err) {
    console.error("All retries failed:", err.message);
    return;
  }

  console.log("Data fetched successfully:", data);
}

Best Practices Demonstrated

  1. Type Safety

    • Always use type parameters with goSync and goFetch
    • Use custom error types with meaningful context
    • Use as const assertions for proper type inference
  2. Error Propagation

    • Return early when encountering errors
    • Preserve error context up the call stack
    • Use appropriate error types for different scenarios
  3. Async Operations

    • Use goFetch for HTTP requests
    • Chain async operations safely
    • Implement retry patterns when needed
  4. Code Organization

    • Keep error handling separate from business logic
    • Use meaningful error types and messages
    • Implement reusable error handling patterns
  5. Variable Declarations

    • Always use let for result declarations
    • Reuse error variable names (typically err)
    • Follow Go-style conventions

See Also