Error Handling in TS-JS
logging and try catching
console.logs should be eliminated (at least in production env). They can give insights to how the app works to malicious users and serve no purpose to real users
a common thing to do is to use a console wrapper that implements all methods (log, error, warn) and add an if statement that checks current env (logger.log)
what happens then to all the catch statements that just log and do nothing else?
catch statements should never just log and do nothing else bc the app may be in a unpredictable state.
catch blocks should focus on 2 critical actions: reporting and failing gracefully. The action depends on whether the error is recoverable or critical.
Instead of dumping logs on browser console you should report the error to a centralized monitoring service.
- backend apps report to a server (ELK stack(elasticsearch, logstash, kibana), datadog)
- front-end and backend apps can report to a 3rd party service (Sentry, Rollbar, Bugsnag)
- Analytics/metrics: Increment a counter or
an event to track the frequency of this error in your analytics platform. (Google Analytics, posthog, mixpanel)
After reporting the catch block should decide on how to handle the user xp:
Critical (unrecoverable) errors:
If the error means the current operation cannot complete (e.g., failed to fetch critical user data), the flow must stop.
- Show an Error Message: Display a user-friendly message (e.g., "We're sorry, we couldn't load your profile. Please try refreshing.") on the screen.
- Revert State: Ensure no partial or corrupted data is saved or displayed. For a form submission failure, you might leave the form open with the existing data.
- Throw a New, Specific Error (Optional): In larger systems, you might catch a low-level error (like an I/O error) and then re-throw a higher-level, application-specific error (like a
DataLoadFailureError) that is handled further up the call stack.
Recoverable Errors:
If the error is minor and the application can continue (e.g., failed to load a non-essential widget), you can handle it more lightly.
- Set a Default: Load a default state or value (e.g., if a user's avatar fails to
, display a generic placeholder image). - Silence the UI: In the most minor cases, simply hide the broken component or widget entirely, preventing visual breakage.
Exceptions vs Errors
| Feature | Error | Exception |
|---|---|---|
| Severity | Critical | Moderate |
| Recoverable? | Usually not | Often |
| Examples | OutOfMemoryError, SyntaxError | FileNotFound, ZeroDivision |
| Handling | Typically not handled | Use try / catch / except |
| When occurs | Compile-time or runtime | Runtime |
JS Error handling patterns
https://betterprogramming.pub/to-throw-or-not-to-throw-error-propagation-in-js-and-ts-68aaabe30e30
https://medium.com/front-end-weekly/error-propagation-in-javascript-with-error-translation-pattern-78cf7178fe92
monad pattern and Effect lib
The most important function in my codebase - Theo t3.gg
This vid goes over the monad pattern implementation Theo uses for his code base
and goes over other solutions as well
Goes over with examples:
- How This avoids long and ugly try catch blocks that we generally must write
- neverthrow
- Effect.ts lib
tryCatch() -> neverthrow -> Effect is a good progression to incrementally add to my code
tryCatch() to start thinking about explicitly handling errors and typing
neverthrow for when the layering (nested error returns) of tryCatch wrappers get more and more complex
Effect you get functional programming pilled, complex wrappers for fetch and logs