At 2 AM, my phone buzzed—production alert. Our payment API was down. I opened CloudWatch logs and saw:
TypeError: Cannot read property 'amount' of undefined
TypeError: Cannot read property 'amount' of undefined
TypeError: Cannot read property 'amount' of undefined
...500 errors in 2 minutes
The bug? A client sent malformed JSON. My code assumed the amount field existed. No validation, no null checks, no error handling. The entire payment service crashed.
After 30 minutes of scrambling, I added validation and proper error handling. The service restarted. Incident resolved.
That night taught me: validation and error handling aren't optional—they're what separates hobby projects from production systems.
This article shares the error handling patterns that have kept my APIs running 24/7.
Input Validation
Why Validate?
Security: Prevent SQL injection, XSS attacks Stability: Prevent crashes from unexpected input User experience: Clear error messages Data integrity: Ensure database consistency
Validation Libraries
I use Zod for TypeScript—runtime validation with type inference.
Schema Definition
Validation Middleware
Using Validation in Routes
Error Classification
Custom Error Classes
Using Custom Errors
Global Error Handler
Using Error Handler
Rate Limiting
Prevent abuse and DOS attacks.
Request Sanitization
Prevent XSS and injection attacks.
Response Formatting
Consistent error responses across the API.
Production Error Logging
Key Takeaways
Always validate input using schemas (Zod, Joi, etc.)
Use custom error classes for different error types
Global error handler catches all errors consistently
Rate limiting prevents abuse
Sanitize input to prevent XSS and injection attacks
Log errors with context for debugging
Hide error details in production
422 for validation errors, 400 for bad requests
Async error handling with try/catch or catchAsync wrapper
Consistent error responses using ResponseFormatter
Next in series: API Documentation and Versioning—making your APIs discoverable and maintainable.
Good error handling turns crashes into graceful failures. Invest time upfront to save hours of debugging later.