🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)
🕵️ SicherheitslückenHak5: Hackers Just Poisoned the Rust Supply Chain | Threat Wire(01.09.2026 um 14:00 Uhr)
🕵️ SicherheitslückenHak5: Hackers Found a Way Into Humanoid Robots | Threat Wire(04.09.2026 um 15:04 Uhr)
🔧 AI Nachrichten Bits und so #1021 (Passwort für Laufwerk)(31.08.2026 um 22:15 Uhr)
🔧 AI Nachrichten Bits und so #1022 (Wie Weißbier)(06.09.2026 um 20:39 Uhr)
🍏 iOS / Mac OSHue-App 6.0 ist da: das sind die Neuerungen(07.09.2026 um 17:21 Uhr)

🔧 Programmierung 🕛 kürzlich 7 Min Lesezeit
0

Error Handling in Node.js: Beyond Try/Catch (2026)

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht




Error Handling in Node.js: Beyond Try/Catch (2026)



Good error handling isn't about catching errors — it's about handling them gracefully so your app stays running.






The Philosophy






CODE
Bad error handling:
→ try/catch everything silently → bugs disappear, no one knows why
→ console.error(err) and move on → logs nobody reads
→ process.exit(1) on any error → crashes production
→ Return null/undefined → caller doesn't know WHY it failed

Good error handling:
→ Every error has a code and context → debugging is fast
→ Errors are categorized → different types get different treatment
→ Recovery is automatic where possible → self-healing apps
→ Users see helpful messages → not stack traces or "something went wrong"









Custom Error Classes






CODE
// Base application error
class AppError extends Error {
constructor(message, statusCode = 500, code = 'INTERNAL_ERROR') {
super(message);
this.name = 'AppError';
this.statusCode = statusCode;
this.code = code;
this.timestamp = new Date().toISOString();
// Capture stack trace (excluding constructor call)
Error.captureStackTrace(this, this.constructor);
}

toJSON() {
return {
error: {
code: this.code,
message: this.message,
statusCode: this.statusCode,
timestamp: this.timestamp,
}
};
}
}

// Domain-specific errors (inherit from AppError)
class NotFoundError extends AppError {
constructor(resource, id) {
super(`${resource} with ID '${id}' not found`, 404, 'NOT_FOUND');
this.resource = resource;
this.id = id;
}
}

class ValidationError extends AppError {
constructor(details) {
const message = Array.isArray(details)
? `${details.length} validation error(s)`
: 'Validation failed';
super(message, 422, 'VALIDATION_ERROR');
this.details = details; // Array of { field, issue }
}
}

class ConflictError extends AppError {
constructor(message) {
super(message || 'Resource conflict', 409, 'CONFLICT');
}
}

class RateLimitError extends AppError {
constructor(retryAfterSeconds) {
super('Too many requests', 429, 'RATE_LIMITED');
this.retryAfter = retryAfterSeconds;
}
}

class AuthenticationError extends AppError {
constructor(message) {
super(message || 'Authentication required', 401, 'UNAUTHORIZED');
}
}

class ForbiddenError extends AppError {
constructor(message) {
super(message || 'Access denied', 403, 'FORBIDDEN');
}
}









Centralized Error Handler (Express)






CODE
// middleware/errorHandler.js
function errorHandler(err, req, res, _next) {
// Request ID for tracing
const requestId = req.headers['x-request-id'] || 'unknown';

// Log the error (structured logging!)
const logData = {
requestId,
path: req.path,
method: req.method,
error: {
name: err.name,
message: err.message,
code: err.code,
stack: err.stack,
},
userId: req.user?.id,
body: sanitizeBody(req.body),
};

if (err.statusCode >= 500) {
// Server errors: full log for debugging
console.error(JSON.stringify(logData));
} else {
// Client errors: info level (expected, not a bug)
console.info(JSON.stringify(logData));
}

// Handle known error types
if (err instanceof AppError) {
const response = { ...err.toJSON(), requestId };

if (err instanceof RateLimitError) {
res.setHeader('Retry-After', err.retryAfter);
}

return res.status(err.statusCode).json(response);
}

// Handle specific library errors
if (err.code === 'SQLITE_CONSTRAINT') {
return res.status(409).json({
error: { code: 'DUPLICATE', message: 'Record already exists', requestId },
});
}

if (err.code === 'ECONNREFUSED') {
return res.status(503).json({
error: { code: 'SERVICE_UNAVAILABLE', message: 'Service temporarily unavailable', requestId },
});
}

// Unknown/unhandled errors: safe generic response
console.error(`[UNHANDLED] ${requestId}: ${err.stack}`);

const isProduction = process.env.NODE_ENV === 'production';
return res.status(500).json({
error: {
code: 'INTERNAL_ERROR',
message: isProduction ? 'An internal error occurred' : err.message,
requestId,
...(isProduction ? {} : { stack: err.stack }),
}
});
}

// Async handler wrapper (eliminates try/catch boilerplate)
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}

// Usage:
app.get('/api/users/:id',
asyncHandler(async (req, res) => {
const user = await userService.findById(req.params.id);
if (!user) throw new NotFoundError('User', req.params.id);
res.json({ success: true, data: user });
})
);

app.use(errorHandler); // Must be last!









Graceful Degradation Patterns






CODE
// Pattern 1: Fallback chain (try multiple sources)
async function getConfig() {
const sources = [
() => fetchFromRedis(),
() => fetchFromDatabase(),
() => fetchFromCacheFile(),
() => ({ /* hardcoded defaults */ }),
];

for (const source of sources) {
try {
const config = await source();
if (config && Object.keys(config).length > 0) return config;
} catch (err) {
console.warn(`Config source failed: ${err.message}`);
continue;
}
}
throw new Error('All config sources failed');
}

// Pattern 2: Circuit breaker (stop hammering failing services)
class CircuitBreaker {
constructor(fn, options = {}) {
this.fn = fn;
this.failureThreshold = options.failureThreshold ?? 5;
this.resetTimeout = options.resetTimeout ?? 30000; // 30s
this.state = 'CLOSED'; // CLOSED | OPEN | HALF_OPEN
this.failures = 0;
this.lastFailureTime = null;
}

async execute(...args) {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime >= this.resetTimeout) {
this.state = 'HALF_OPEN'; // Try one request
} else {
throw new Error('Circuit breaker OPEN - service unavailable');
}
}

try {
const result = await this.fn(...args);
this.onSuccess();
return result;
} catch (err) {
this.onFailure(err);
throw err;
}
}

onSuccess() {
this.failures = 0;
this.state = 'CLOSED';
}

onFailure(err) {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.failureThreshold) {
this.state = 'OPEN';
console.warn(`Circuit breaker OPEN after ${this.failures} failures`);
}
}
}

const apiBreaker = new CircuitBreaker(() => externalAPI.call());
await apiBreaker.execute(); // Auto-fails fast when service is down

// Pattern 3: Retry with backoff
async function retryWithBackoff(fn, options = {}) {
const {
maxAttempts = 3,
baseDelay = 1000,
maxDelay = 10000,
shouldRetry = (err) => !['4xx'].includes(err.statusCode),
} = options;

let lastError;

for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
lastError = err;

if (!shouldRetry(err) || attempt === maxAttempts) throw err;

const delay = Math.min(baseDelay * 2 ** (attempt - 1), maxDelay);
const jitter = delay * (0.5 + Math.random() * 0.5); // Add jitter
console.warn(`Attempt ${attempt}/${maxAttempts} failed, retrying in ${Math.round(jitter)}ms`);
await sleep(jitter);
}
}

throw lastError;
}

// Usage:
const data = await retryWithBackoff(
() => fetchFromUnstableAPI(),
{ maxAttempts: 5, baseDelay: 1000, shouldRetry: (e) => e.code !== 'NOT_FOUND' }
);









Unhandled Rejection & Uncaught Exception






CODE
// These MUST be set up at the top of your entry file
// They are your last line of defense

process.on('unhandledRejection', (reason, promise) => {
console.error('[UNHANDLED REJECTION]', reason);
// Don't crash! Log it and decide based on severity
// In development: crash loudly
if (process.env.NODE_ENV === 'development') {
throw reason; // Crashes with full stack trace
}
// In production: log and continue (or gracefully shutdown)
});

process.on('uncaughtException', (error) => {
console.error('[UNCAUGHT EXCEPTION]', error);
// Best practice: shutdown gracefully
// The process may be in an undefined state
setTimeout(() => {
process.exit(1); // Force exit if graceful shutdown takes too long
}, 1000).unref(); // Don't keep process alive just for this

// Cleanup before exit:
// - Close database connections
// - Flush logs
// - Notify monitoring service
server.close(() => {
console.log('Server closed due to uncaught exception');
process.exit(1);
});
});

// Graceful shutdown handler
function setupGracefulShutdown(server) {
const shutdown = (signal) => {
console.log(`${signal} received, shutting down gracefully...`);

// Stop accepting new connections
server.close(() => {
console.log('HTTP server closed');
process.exit(0);
});

// Force exit after timeout
setTimeout(() => {
console.error('Forced shutdown after timeout');
process.exit(1);
}, 30000).unref();
};

process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
}









Structured Logging






CODE
// logger.js — simple structured logger
const levels = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 };
const currentLevel = levels[process.env.LOG_LEVEL] || levels.INFO;

function log(level, message, meta = {}) {
if (levels[level] < currentLevel) return;

const entry = {
timestamp: new Date().toISOString(),
level,
message,
...meta,
pid: process.pid,
hostname: require('os').hostname(),
};

const line = JSON.stringify(entry);

switch (level) {
case 'ERROR': case 'WARN': console.error(line); break;
default: console.log(line);
}
}

export const logger = {
debug: (msg, meta) => log('DEBUG', msg, meta),
info: (msg, meta) => log('INFO', msg, meta),
warn: (msg, meta) => log('WARN', msg, meta),
error: (msg, meta) => log('ERROR', msg, meta),
};

// Usage in error handlers:
logger.error('Database connection failed', {
host: dbHost,
port: dbPort,
errorCode: err.code,
retryCount: attempts,
});
// Output: {"timestamp":"...","level":"ERROR","message":"Database connection failed","host":"db.example.com","port":5432,"errorCode":"ECONNREFUSED","retryCount":3,...}
// This is parseable by log aggregation tools (Datadog, CloudWatch, etc.)









Quick Checklist






CODE
Before shipping error handling:

□ Custom error classes with status codes and machine-readable codes?
□ Global error handler catches ALL unhandled errors?
□ Async errors wrapped (asyncHandler or equivalent)?
□ No raw stack traces exposed to users in production?
□ Every error logged with enough context to debug?
□ Request IDs included in all error responses?
□ Circuit breaker or rate limiting on external calls?
□ Retry logic for transient failures?
□ Graceful shutdown on SIGTERM/SIGINT?
□ UnhandledRejection listener registered?
□ UncaughtException listener registered?
□ Error responses follow consistent JSON format?
□ Client errors (4xx) vs server errors (5xx) handled differently?

Score yourself: Each ☑️ is a production-ready practice.









What's the worst production error you've debugged? How did you find it?



Follow @armorbreak for more practical developer guides.

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Hackers Just Poisoned the Rust Supply Chain | Threat Wire
1 Quelle
Hackers Found a Way Into Humanoid Robots | Threat Wire
1 Quelle
Bits und so #1021 (Passwort für Laufwerk)
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Error Handling in Node.js: Beyond Try/Catch (2026)

Thematisch verwandte Begriffe: Error, Handling, Nodejs, Beyond · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...