Code-Along 1 - API Error Handling

Introduction to API Error Handling

Why Error Handling Matters

Error handling is a critical part of any application that interacts with external APIs. Well-implemented error handling:

  • Improves user experience by providing meaningful feedback
  • Prevents application crashes
  • Makes debugging easier
  • Increases application reliability

When working with APIs, a number of things can go wrong: network issues, server problems, invalid requests, authentication failures, etc. Properly handling these errors ensures your application remains functional and user-friendly even when things don't go as expected.

HTTP Status Codes

Understanding HTTP Status Codes

HTTP status codes are standardized responses from servers that indicate the outcome of an HTTP request. They are grouped into five classes:

  • 1xx (Informational): Request received, continuing process
  • 2xx (Success): Request successfully received, understood, and accepted
  • 3xx (Redirection): Further action needs to be taken to complete the request
  • 4xx (Client Error): Request contains bad syntax or cannot be fulfilled
  • 5xx (Server Error): Server failed to fulfill a valid request
// Common HTTP Status Codes
200 - OK                   // Request succeeded
201 - Created              // Resource created successfully
400 - Bad Request          // Server couldn't understand the request
401 - Unauthorized         // Authentication required
403 - Forbidden            // Server understood but refuses to authorize
404 - Not Found            // Resource not found
500 - Internal Server Error // Server encountered an unexpected condition
                

Error Handling in React

Implementing Error Handling with Fetch API

When working with the Fetch API in React, it's important to understand that fetch doesn't automatically reject on HTTP error statuses. You need to check if the response is ok (status in 200-299 range) and throw an error manually if needed.

Best practices for error handling with fetch:

  • Use try/catch blocks to capture both network errors and your thrown HTTP errors
  • Track loading states to provide feedback to users
  • Store error information in state to display to users
  • Consider different types of errors and handle them appropriately
// Complete Error Handling Example
import { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      setError(null);
      
      try {
        const response = await fetch('https://api.example.com/data');
        
        // Check if the response is ok (status in the range 200-299)
        if (!response.ok) {
          // Get more details about the error
          const errorData = await response.json().catch(() => null);
          throw new Error(
            errorData?.message || `HTTP error! status: ${response.status}`
          );
        }
        
        const data = await response.json();
        setData(data);
      } catch (err) {
        setError(err.message || 'Something went wrong');
        // You might also want to log the error for debugging
        console.error('Fetch error:', err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, []);

  if (loading) return <p>Loading...</p>
  if (error) return <p>Error: {error}</p>
  if (!data) return <p>No data available</p>

  return (
    <div>
      {/* Render your data here */}
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
                

Error Boundaries

React Error Boundaries

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of crashing the component tree.

Key points about error boundaries:

  • They catch errors during rendering, in lifecycle methods, and in constructors
  • They do NOT catch errors in event handlers, asynchronous code, or server-side rendering
  • They help isolate errors to specific parts of your UI
  • They require class components (cannot be implemented with hooks)
// Example of Error Boundary Component
import React from 'react';

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

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can log the error to an error reporting service
    console.error('Error caught by ErrorBoundary:', error, errorInfo);
    this.setState({
      error: error,
      errorInfo: errorInfo
    });
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return (
        <div className="error-container">
          <h2>Something went wrong.</h2>
          {this.props.fallback || <p>Please try again later.</p>}
          {process.env.NODE_ENV === 'development' && (
            <details style={{ whiteSpace: 'pre-wrap' }}>
              {this.state.error && this.state.error.toString()}
              <br />
              {this.state.errorInfo && this.state.errorInfo.componentStack}
            </details>
          )}
        </div>
      );
    }

    return this.props.children;
  }
}

// Usage
function App() {
  return (
    <ErrorBoundary>
      <MyComponent />
    </ErrorBoundary>
  );
}
                

Best Practices for API Error Handling

Implementing Robust Error Handling

  • Centralize error handling - Create utility functions for common error handling patterns
  • Provide meaningful error messages - Help users understand what went wrong
  • Include retry mechanisms - For transient errors, consider adding retry functionality
  • Log errors - Maintain logs for debugging purposes
  • Different UI for different errors - Show appropriate UI based on error type (e.g., network error vs. validation error)
  • Graceful degradation - Ensure your app still functions when parts of it can't fetch data

Resources

Code-Along Project

Complete the API Error Handling code-along to practice implementing error handling in a React application.

Starter Code Solution Code