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 for HTTP errors
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        
        // Parse the JSON
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message || 'Something went wrong');
        console.error('Error fetching data:', err);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, []);

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

  return (
    <div>
      <h2>Data Loaded Successfully</h2>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}
                

Advanced Error Handling with Axios

Using Axios for API Requests

Axios is a popular alternative to the Fetch API that provides some additional features and more intuitive error handling. Unlike fetch, Axios automatically throws errors for non-2xx status codes.

  • Automatically parses JSON responses
  • Throws errors for HTTP error status codes
  • Provides more detailed error information
  • Supports request and response interceptors

// Axios Error Handling Example
import { useState, useEffect } from 'react';
import axios from 'axios';

function AxiosDataFetcher() {
  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 axios.get('https://api.example.com/data');
        setData(response.data);
      } catch (err) {
        // Axios provides more detailed error information
        if (err.response) {
          // The request was made and the server responded with a status code
          // that falls out of the range of 2xx
          console.error('Error data:', err.response.data);
          console.error('Error status:', err.response.status);
          setError(`Server error: ${err.response.status}`);
        } else if (err.request) {
          // The request was made but no response was received
          console.error('Error request:', err.request);
          setError('No response from server');
        } else {
          // Something happened in setting up the request
          console.error('Error message:', err.message);
          setError('Request configuration error');
        }
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, []);

  // Render UI based on state
  // ...
}
                

User Feedback and Error UI

Designing Effective Error Messages

Good error handling isn't just about catching errors; it's also about presenting them to users in a way that is:

  • Clear: Use plain language that explains what went wrong
  • Helpful: Provide guidance on what to do next
  • Context-appropriate: Different errors may need different levels of detail
  • Actionable: When possible, offer a way to resolve the issue

// Error UI Component Example
function ErrorDisplay({ error, retry }) {
  // Different UI based on error type
  if (error.includes('network') || error.includes('offline')) {
    return (
      <div className="error-container network-error">
        <h3>Connection Problem</h3>
        <p>Please check your internet connection and try again.</p>
        <button onClick={retry}>Retry</button>
      </div>
    );
  }
  
  if (error.includes('401') || error.includes('403')) {
    return (
      <div className="error-container auth-error">
        <h3>Authentication Error</h3>
        <p>You don't have permission to access this resource.</p>
        <button onClick={() => window.location.href = '/login'}>Log In</button>
      </div>
    );
  }
  
  // Default error display
  return (
    <div className="error-container">
      <h3>Something Went Wrong</h3>
      <p>{error}</p>
      <button onClick={retry}>Try Again</button>
    </div>
  );
}
                

Project: Implementing Error Handling

Let's apply what we've learned by building a weather app that demonstrates robust error handling.

Project Repository Weather API Documentation