# Error Handling Quick Reference ## Backend ### Import Error Handler ```javascript const { ErrorResponses, asyncHandler, sanitizeError } = require('../utils/errorHandler'); const logger = require('../utils/logger'); ``` ### Standard Route Pattern ```javascript router.get('/resource/:id', asyncHandler(async (req, res) => { const resource = await findResource(req.params.id); if (!resource) { throw ErrorResponses.notFound('Resource not found'); } res.json(resource); })); ``` ### Error Types ```javascript ErrorResponses.badRequest('Invalid input') // 400 ErrorResponses.unauthorized('Auth required') // 401 ErrorResponses.forbidden('No permission') // 403 ErrorResponses.notFound('Not found') // 404 ErrorResponses.conflict('Already exists') // 409 ErrorResponses.unprocessable('Cannot process') // 422 ErrorResponses.tooManyRequests('Rate limited') // 429 ErrorResponses.internal('Server error') // 500 ErrorResponses.serviceUnavailable('Unavailable') // 503 ``` ### Logging ```javascript // Standard logging logger.info('Operation completed', { userId, action }); logger.error('Operation failed', { error, context }); logger.warn('Unusual activity', { details }); // Security logging logger.security('login_attempt', { userId, ip, success: true }); // Performance logging const start = Date.now(); await operation(); logger.performance('operation_name', Date.now() - start, { details }); ``` ### DO NOT ```javascript // ❌ Bad - Exposes stack trace res.status(500).json({ error: error.message }); // ❌ Bad - Exposes file path console.error('Failed:', error.stack); // ❌ Bad - Exposes database details res.json({ error: `Database error: ${err.code}` }); // ✅ Good - Sanitized throw ErrorResponses.internal('Operation failed'); ``` ## Frontend ### Import Error Handler ```javascript import { useErrorNotification } from '../components/ErrorNotificationProvider'; import { getErrorMessage, isAuthError } from '../utils/errorHandler'; ``` ### Component Pattern ```javascript function MyComponent() { const { showError, showSuccess } = useErrorNotification(); const handleAction = async () => { try { const response = await api.post('/endpoint', data); showSuccess('Operation successful'); return response.data; } catch (error) { showError(error); // Or with custom message: showError(error, { defaultMessage: 'Custom error message', title: 'Operation Failed' }); } }; } ``` ### Error Checking ```javascript import { isAuthError, isPermissionError, isValidationError } from '../utils/errorHandler'; try { await api.post('/endpoint', data); } catch (error) { if (isAuthError(error)) { // Handle auth error } else if (isPermissionError(error)) { // Handle permission error } else { // Handle other errors } } ``` ### Retry Pattern ```javascript import { retryRequest } from '../utils/errorHandler'; const data = await retryRequest( () => api.get('/endpoint'), { maxRetries: 3, retryDelay: 1000 } ); ``` ### Custom Error Messages ```javascript const { showError, showWarning, showInfo } = useErrorNotification(); // Error notification showError(error, { defaultMessage: 'Failed to load data', duration: 6000 }); // Warning notification showWarning('You are offline', { title: 'Connection Lost', duration: 5000 }); // Info notification showInfo('Saving in progress...', { duration: 3000 }); ``` ## Translation Keys ### English (en.json) ```json "errors": { "general": { "unexpected", "tryAgain", "contactSupport" }, "network": { "title", "message", "timeout" }, "auth": { "title", "required", "sessionExpired" }, "permission": { "title", "message", "adminRequired" }, "validation": { "title", "invalidInput" }, "server": { "title", "unavailable" } } ``` ### Romanian (ro.json) All error keys have Romanian translations ## Common Patterns ### File Upload with Error Handling ```javascript // Backend router.post('/upload', asyncHandler(async (req, res) => { if (!req.files || !req.files.file) { throw ErrorResponses.badRequest('No file uploaded'); } const file = req.files.file; if (file.size > MAX_SIZE) { throw ErrorResponses.badRequest('File too large'); } await saveFile(file); res.json({ success: true }); })); // Frontend const handleUpload = async (file) => { try { const formData = new FormData(); formData.append('file', file); await api.post('/upload', formData); showSuccess('File uploaded successfully'); } catch (error) { showError(error, { defaultMessage: 'Failed to upload file' }); } }; ``` ### Database Query with Error Handling ```javascript // Backend router.get('/users', asyncHandler(async (req, res) => { const users = await new Promise((resolve, reject) => { db.all('SELECT * FROM users', [], (err, rows) => { if (err) reject(err); else resolve(rows); }); }); res.json(users); })); // Errors are automatically caught and sanitized by asyncHandler ``` ### Form Validation ```javascript // Backend const { body, validationResult } = require('express-validator'); router.post('/users', [ body('email').isEmail(), body('username').isLength({ min: 3 }) ], asyncHandler(async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { throw ErrorResponses.badRequest('Validation failed'); } // Process valid data })); // Frontend const handleSubmit = async (data) => { // Client-side validation if (!data.email) { showError({ response: { status: 400 } }, { defaultMessage: 'Email is required' }); return; } try { await api.post('/users', data); showSuccess('User created'); } catch (error) { showError(error); } }; ``` ## Testing ### Test Error Responses ```bash # Should return sanitized error curl http://localhost:12345/api/nonexistent # Should not expose stack traces or file paths curl http://localhost:12345/api/users -H "Authorization: Bearer invalid" # Check logs (internal details preserved) docker exec streamflow tail -f /app/logs/error.log ``` ### Test Frontend Error Handling ```javascript // In browser console try { await fetch('/api/nonexistent'); } catch (error) { console.log('Error handled:', error); } // Should show user-friendly message, not technical details ``` ## Security Checklist - ✅ Never send `error.stack` to client - ✅ Never expose file paths - ✅ Never expose database queries - ✅ Always use `asyncHandler` for async routes - ✅ Always use `ErrorResponses` for errors - ✅ Always sanitize user input - ✅ Log full details internally only - ✅ Use translations for user messages - ✅ Test in both development and production modes ## Need Help? See full documentation: `docs/ERROR_HANDLING_SECURITY.md`