Extending Error Handling with a Custom Error Generator: Tips & Templates

Extending Error Handling with a Custom Error Generator — Tips & Templates

Why use a Custom Error Generator

  • Consistency: produce uniform error objects across services.
  • Rich context: attach status, codes, user-facing messages, internal details, and metadata.
  • Easier handling: centralized checks (instanceof / error.code) in middleware and alerts.
  • Testability: deterministic error shapes simplify unit tests and mocks.

Design principles

  • Extend the native Error so instanceof works and stack is preserved.
  • Separate operational vs programmer errors (only operational errors are handled and returned to clients).
  • Include stable machine-readable codes (e.g., AUTH_INVALID, DBTIMEOUT) plus human messages.
  • Avoid leaking internals to users; provide a safe userMessage and a developerMessage/details.
  • Make errors serializable (JSON-friendly fields only) for logging and transport.
  • Capture origin metadata (service, function, correlationId, request info) where appropriate.

Minimal JS/TS template (generator + base class)

javascript

// BaseError.js class BaseError extends Error { constructor({ code, status = 500, message, userMessage = ‘An error occurred’, details }) { super(message); this.name = this.constructor.name; this.code = code; // machine code this.status = status; // HTTP status or equivalent this.userMessage = userMessage; this.details = details; // internal/debug info Error.captureStackTrace?.(this, this.constructor); } toJSON() { return { name: this.name, code: this.code, status: this.status, userMessage: this.userMessage }; } } module.exports = BaseError;

javascript

// errorGenerator.js const BaseError = require(’./BaseError’); const errorTemplates = { NOT_FOUND: ({ resource }) => new BaseError({ code: ‘NOT_FOUND’, status: 404, message: </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">resource </span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">||</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation" style="color: rgb(163, 21, 21);">'Resource'</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);"> not found</span><span class="token template-string template-punctuation" style="color: rgb(163, 21, 21);">, userMessage: ‘Requested resource not found’, details: { resource } }), VALIDATION: ({ field, reason }) => new BaseError({ code: ‘VALIDATION_ERROR’, status: 400, message: </span><span class="token template-string" style="color: rgb(163, 21, 21);">Invalid </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">field</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);">: </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">reason</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string template-punctuation" style="color: rgb(163, 21, 21);">, userMessage: ‘Invalid input’, details: { field, reason } }), // add more templates… }; function createError(type, opts = {}) { const factory = errorTemplates[type]; if (!factory) throw new Error(</span><span class="token template-string" style="color: rgb(163, 21, 21);">Unknown error type: </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">type</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string template-punctuation" style="color: rgb(163, 21, 21);">); return factory(opts); } module.exports = { createError, errorTemplates };

Usage examples

  • Throw in business logic:
    • throw createError(‘NOTFOUND’, { resource: ‘User’ });
  • Express error middleware:

javascript

app.use((err, req, res, next) => { if (err instanceof BaseError) { logger.error(err); // structured logging including err.details return res.status(err.status).json({ code: err.code, message: err.userMessage }); } logger.error({ err, uncaught: true }); res.status(500).json({ code: ‘INTERNAL_ERROR’, message: ‘Something went wrong’ }); });

Tips & best practices

  • Centralize templates: one source for all error shapes and messages.
  • Version error codes if clients depend on them.
  • Map internal codes → HTTP statuses in a single place.
  • Strip stacks/details for production responses; keep them in logs.
  • Add correlation IDs to error objects for tracing across services.
  • Test error serialisation and middleware behavior in CI.
  • Document codes and userMessages for API consumers.

Quick checklist for production readiness

  • Error codes cataloged and documented
  • Central generator + typed templates (TS) or runtime validation
  • Structured logging with correlation IDs
  • Error handling middleware that distinguishes operational vs fatal errors
  • Tests for thrown errors, error responses, and logging

If you want, I can: produce a TypeScript version, expand the templates for HTTP APIs, or generate an errors catalog for a specific service—pick one.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *