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.

🚀 Rohit Singh 🚀 – Medium

Read writing from 🚀 Rohit Singh 🚀 on Medium. Full-stack developer with 6+ years in Angular, Node.js & AWS. Sharing tips, best practices & real-world lessons from building scalable apps.

favicon medium.com

Top comments (0)