DEV Community

Cover image for JavaScript Error Handling Patterns You Must Know (With Examples & Best Practices)
ROHIT SINGH
ROHIT SINGH

Posted on

JavaScript Error Handling Patterns You Must Know (With Examples & Best Practices)

Errors are an unavoidable part of software development. Whether it’s a typo, a failed API call, or unexpected user input, JavaScript errors can break your application if not handled properly.
Good error handling ensures your app is reliable, debuggable, and user-friendly.

In this blog, we’ll explore essential error handling patterns in JavaScript, complete with examples and best practices.

πŸ”Ή 1. The Classic try...catch

The most common way to handle errors in JavaScript is using try...catch.

function parseJSON(data) {
  try {
    return JSON.parse(data);
  } catch (error) {
    console.error("Invalid JSON:", error.message);
    return null;
  }
}

console.log(parseJSON('{ "name": "Anshul" }')); // βœ… Works
console.log(parseJSON("invalid-json"));         // ❌ Error handled gracefully

Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:

Always provide a fallback when something goes wrong.

Log errors with context, not just a generic message.

πŸ”Ή 2. Using finally

finally is executed regardless of success or failure, useful for cleanup.

function fetchData() {
  try {
    console.log("Fetching data...");
    throw new Error("Network issue!");
  } catch (error) {
    console.error("Error:", error.message);
  } finally {
    console.log("Cleanup resources, close connections, etc.");
  }
}
fetchData();

Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Use finally to release resources like file handles, database connections, or loading states.

πŸ”Ή 3. Error Handling in Async/Await

When using async/await, wrap code in try...catch.

async function getUser() {
  try {
    const res = await fetch("https://api.example.com/user");
    const data = await res.json();
    console.log(data);
  } catch (error) {
    console.error("Failed to fetch user:", error.message);
  }
}
getUser();
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Always assume network calls can fail. Show user-friendly error messages instead of crashing.

πŸ”Ή 4. Centralized Error Handling

Instead of scattering try...catch everywhere, use a central handler.

function handleError(error) {
  console.error("Global Error Handler:", error.message);
  // Send error to monitoring service like Sentry
}

async function safeExecute(fn) {
  try {
    await fn();
  } catch (error) {
    handleError(error);
  }
}

// Usage
safeExecute(async () => {
  throw new Error("Something broke!");
});

Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Centralized handlers make debugging easier and integrate well with logging/monitoring tools.

πŸ”Ή 5. Graceful Degradation with Default Values

Sometimes, instead of crashing, fallback to a default value.

function getUserName(user) {
  try {
    return user.profile.name;
  } catch {
    return "Guest"; // fallback
  }
}

console.log(getUserName({ profile: { name: "Anshul" } })); // "Anshul"
console.log(getUserName(null));                           // "Guest"

Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Use fallbacks for non-critical failures (e.g., missing optional fields).
For critical failures, log them properly.

πŸ”Ή 6. Error Boundaries in Frontend Apps

In React, Angular, Vue, use error boundaries to prevent full app crashes.

Example in React:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.error("Error caught:", error, info);
  }

  render() {
    if (this.state.hasError) {
      return <h2>Something went wrong!</h2>;
    }
    return this.props.children;
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Use error boundaries in UI frameworks to catch render-time crashes.

πŸ”Ή 7. Custom Error Classes

Create custom error types for better debugging.

class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

function validateAge(age) {
  if (age < 18) {
    throw new ValidationError("Age must be 18+");
  }
  return true;
}

try {
  validateAge(16);
} catch (error) {
  if (error instanceof ValidationError) {
    console.error("Validation failed:", error.message);
  } else {
    throw error;
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ Best Practice:
Use custom errors for business logic checks (e.g., validation, permissions).

βœ… Final Best Practices for Error Handling in JavaScript

πŸ”Έ Don’t swallow errors silently – always log them.

πŸ”Έ Use meaningful error messages with context.

πŸ”Έ Centralize error handling for maintainability.

πŸ”Έ Use custom error classes for better debugging.

πŸ”Έ Integrate with monitoring tools (Sentry, LogRocket, etc.).

πŸ”Έ Show user-friendly messages, not stack traces.

πŸš€ Conclusion

Error handling is not just about preventing crashesβ€”it’s about building resilient, debuggable, and user-friendly applications.
By applying these patterns and best practices, you’ll write cleaner, safer JavaScript code that handles the unexpected gracefully.

Top comments (0)