Tools
Tools: Designing a Universal Error Handler for Frontend & Backend (React + Node.js)
2026-01-22
0 views
admin
The Real Problem with Error Handling ## Backend problems ## Frontend problems ## The Key Insight: Errors Are a Contract ## Design Principle #1: Shared Contract, Not Tight Coupling ## Design Principle #2: Backend Decides What Happened ## Design Principle #3: Frontend Decides How to Show It ## Design Principle #4: Progressive Adoption ## Design Principle #5: Hooks-Only, Modern React ## Design Principle #6: TypeScript-First ## End-to-End Flow (How Everything Connects) ## Why This Matters in Production ## The Result: A Universal Error Handler ## Final Thoughts Error handling is one of those things we all do — but rarely design properly. In most projects, error handling grows organically: This article walks through how and why I designed a universal error-handling system that works across Backend (Node.js / Express / Next.js APIs) and Frontend (React / Next.js UI) — using a shared contract, not tight coupling. Most applications suffer from the same issues: 👉 The issue is lack of design. Instead of treating errors as exceptions, I started treating them as data.
That led to one fundamental idea: Frontend and backend should share an error contract, not implementations. This single decision shaped the entire system. The frontend and backend do not depend on each other’s code.
They only agree on: Example error contract: The backend is the source of truth. Its responsibility is to: It does not decide how the error is shown. Everything becomes a single, predictable error object. The frontend owns user experience. It receives structured error data and decides: This allows the same backend error to be shown differently in: Technical message ≠ UI message — and that’s intentional. This system is not all-or-nothing. Even if the backend doesn’t follow the contract perfectly, the frontend falls back safely. This makes the library practical for: All frontend APIs are built with hooks: Runtime UI crashes are handled via a React error boundary, wrapped once at the app root. TypeScript isn’t an add-on — it’s the foundation. Types are the documentation. Here’s the full journey of an error: Backend detects a failure At no point does the UI guess what went wrong. This approach gives you: Error handling stops being “glue code” and becomes infrastructure. I packaged this system as an open-source npm library: GitHub repo: https://github.com/shubhamgupta-oss/universal-error-handler
NPM package: https://www.npmjs.com/package/@shubhamgupta-oss/universal-error-handler Good error handling isn’t about catching errors. Once you treat errors as a first-class system, everything else becomes simpler. If you’re building full-stack applications and struggling with inconsistent error handling, I hope this approach helps. Feedback, suggestions, and contributions are welcome. 🙌 Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse CODE_BLOCK:
{ "success": false, "message": "User already exists", "code": "DUPLICATE_ERROR", "details": { "field": "email" }, "traceId": "abc-123"
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ "success": false, "message": "User already exists", "code": "DUPLICATE_ERROR", "details": { "field": "email" }, "traceId": "abc-123"
} CODE_BLOCK:
{ "success": false, "message": "User already exists", "code": "DUPLICATE_ERROR", "details": { "field": "email" }, "traceId": "abc-123"
} COMMAND_BLOCK:
npm install @shubhamgupta-oss/universal-error-handler Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
npm install @shubhamgupta-oss/universal-error-handler COMMAND_BLOCK:
npm install @shubhamgupta-oss/universal-error-handler - a few try/catch blocks
- some HTTP status checks
- random error messages sent from the backend
- UI logic guessing what went wrong
Eventually, the system becomes fragile. - Errors from DB, validation, or external APIs look different
- Internal stack traces leak to clients
- Error responses change over time
- Hard to debug production issues - Every API returns a different error shape
- UI shows technical or confusing messages
- Error handling logic is duplicated everywhere
- Runtime crashes cause white screens
The core issue isn’t missing try/catch. - error codes
- error structure - consistency
- backward compatibility
- freedom to evolve each layer independently - detect what went wrong
- categorize the error
- attach structured metadata
- assign a stable error code - Internally, the backend normalizes:
- database errors
- validation failures
- authentication & authorization errors
- third-party API failures
- system-level crashes - the message shown to users
- localization (i18n)
- UI presentation (toast, banner, modal)
- retry or recovery behavior - admin dashboards
- consumer apps
- mobile vs desktop UIs - use only backend utilities
- use only frontend hooks
- use both together for best results - legacy systems
- gradual refactors
- monorepos with mixed stacks - no class components
- no outdated patterns - useAPIError() – handle API & network errors
- useAsyncData() – async operations with built-in error state
- useGlobalError() – app-wide error state
- useNetworkStatus() – offline/online detection - strict typing
- shared types between FE & BE
- discriminated unions for error types
- strong inference for consumers - fewer runtime surprises
- safer refactors
- better IDE experience - Error is normalized into a standard structure
- API returns a predictable error response
- Frontend parses the error
- Error code is mapped to a user-friendly message
- UI displays a safe, meaningful response - cleaner APIs
- safer production behavior
- easier debugging (via trace IDs)
- a shared mental model for teams - React 18 & 19 - defining responsibility
- designing contracts
- respecting separation of concerns
how-totutorialguidedev.toainetworknodedatabasegitgithub