Skip to main content

Errors happen everywhere

In a full-stack app, errors can occur at every layer:
Good error handling means the user always sees a clear, helpful message — regardless of where the error originated.

Layer 1: Backend errors (FastAPI)

FastAPI provides structured error responses. Use HTTPException for expected errors:
Pydantic handles validation errors automatically. If someone sends { "name": 123 } instead of a string, FastAPI returns a 422 with details — you don’t need to code this.

Common HTTP status codes for errors

Write clear detail messages. They travel all the way to the user’s screen. “A user with this email already exists” is better than “Conflict” or “Error 409”.

Layer 2: API client (JavaScript)

Your API client translates HTTP errors into JavaScript errors:
This handles three types of errors:
  1. Network errorsfetch() itself throws (no response at all)
  2. Validation errors — 422 with field-level details from Pydantic
  3. Other HTTP errors — 404, 409, 500, etc. with detail message from FastAPI

Layer 3: React components

Components catch errors and display them to the user:
Two error display zones:
  • Global errors (top of form) — network errors, server errors, unexpected issues
  • Field errors (next to inputs) — validation errors for specific fields

Page-level error handling

For data fetching errors, use the early return pattern:
The “Try Again” button calls loadUsers() directly. It resets the error, shows loading, and retries the fetch. Simple and effective.

The error flow — from backend to user

The detail message you write in Python shows up directly on the user’s screen. Write it for humans, not for developers.

Error handling checklist

Every component that makes API calls should handle:
  • Network failures — “Unable to connect” message with retry option
  • Validation errors — field-level messages next to inputs
  • Not found (404) — “Record not found” message
  • Conflict (409) — “Already exists” message
  • Server errors (500) — “Something went wrong” with retry option
  • Loading state — disable buttons, show spinners during requests
Never show raw error messages like “HTTP 500” or stack traces to users. Always translate technical errors into human-readable messages. The API client is the right place to do this translation.

What’s next?

Errors are handled. The last piece of polish: loading states throughout your app — spinners, skeleton screens, and disabled buttons that make your app feel professional.

Loading states everywhere

Provide consistent loading feedback throughout your full-stack application