or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-koa-onerror

Comprehensive error handling middleware for Koa.js applications that overrides ctx.onerror with flexible response formatting

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/koa-onerror@5.0.x

To install, run

npx @tessl/cli install tessl/npm-koa-onerror@5.0.0

index.mddocs/

Koa Onerror

Koa Onerror is an error handling middleware for Koa.js applications that overrides the default ctx.onerror method to provide comprehensive error handling. It offers flexible error response formatting with automatic content negotiation, supports both development and production modes, and handles stream errors that can't be caught by traditional try-catch blocks.

Package Information

  • Package Name: koa-onerror
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install koa-onerror

Core Imports

import { onerror } from "koa-onerror";
// Also available: OnerrorError, OnerrorHandler, OnerrorOptions types

For CommonJS:

const { onerror } = require("koa-onerror");

Basic Usage

import Koa from "koa";
import { onerror } from "koa-onerror";

const app = new Koa();

// Apply error handling middleware
onerror(app);

// Your other middleware
app.use(async (ctx) => {
  // This error will be handled by koa-onerror
  throw new Error("Something went wrong!");
});

app.listen(3000);

Architecture

Koa Onerror works by replacing the default ctx.onerror method with a more sophisticated error handler that:

  • Content Negotiation: Automatically responds with appropriate format (HTML, JSON, text) based on Accept headers
  • Error Normalization: Converts non-Error objects into proper Error instances
  • Stream Cleanup: Handles and cleans up request streams during error scenarios using stream-wormhole
  • Development vs Production: Shows detailed error information in development, sanitized responses in production
  • Status Code Management: Automatically sets appropriate HTTP status codes with fallback to 500
  • Header Propagation: Forwards custom headers from error objects to the response

Capabilities

Primary Middleware Function

Main function that configures comprehensive error handling for a Koa application.

/**
 * Configure error handling middleware for a Koa application
 * @param app - Koa application instance
 * @param options - Optional configuration for error handlers
 * @returns The modified Koa application instance
 */
function onerror(app: any, options?: OnerrorOptions): any;

Usage Example:

import Koa from "koa";
import { onerror } from "koa-onerror";

const app = new Koa();

// Basic usage with default handlers
onerror(app);

// With custom options
onerror(app, {
  // Custom JSON error handler
  json(err, ctx) {
    ctx.body = {
      success: false,
      error: err.message,
      code: err.status || 500
    };
  },
  
  // Redirect for HTML/text errors
  redirect: "/error-page",
  
  // Universal error handler (overrides content negotiation)
  all(err, ctx) {
    console.error("Error occurred:", err);
    ctx.body = "An error occurred";
  }
});

Error Object Types

Enhanced error object interface used throughout the error handling system.

/**
 * Extended Error object with additional properties for HTTP error handling
 */
type OnerrorError = Error & {
  /** HTTP status code for the error response */
  status: number;
  /** Optional HTTP headers to include in the error response */
  headers?: Record<string, string>;
  /** Whether to expose detailed error information to clients */
  expose?: boolean;
};

Error Handler Types

Function signature for custom error handling callbacks.

/**
 * Custom error handler function signature
 * @param err - The error object with additional HTTP properties
 * @param ctx - Koa context object
 */
type OnerrorHandler = (err: OnerrorError, ctx: any) => void;

Configuration Options

Comprehensive configuration interface for customizing error handling behavior.

/**
 * Configuration options for koa-onerror middleware
 */
type OnerrorOptions = {
  /** Custom handler for text/plain responses */
  text?: OnerrorHandler;
  /** Custom handler for application/json responses */
  json?: OnerrorHandler;
  /** Custom handler for text/html responses */
  html?: OnerrorHandler;
  /** Universal handler that bypasses content negotiation */
  all?: OnerrorHandler;
  /** Custom handler for application/javascript responses (JSONP) */
  js?: OnerrorHandler;
  /** Redirect URL for HTML/text errors instead of showing error page */
  redirect?: string | null;
  /** Custom content negotiation function */
  accepts?: (...args: string[]) => string;
};

Detailed Option Usage:

onerror(app, {
  // Override default text handler
  text(err, ctx) {
    ctx.body = `Error ${err.status}: ${err.message}`;
  },
  
  // Override default JSON handler
  json(err, ctx) {
    ctx.body = {
      error: {
        message: err.message,
        status: err.status,
        timestamp: new Date().toISOString()
      }
    };
  },
  
  // Override default HTML handler
  html(err, ctx) {
    ctx.body = `
      <h1>Error ${err.status}</h1>
      <p>${err.message}</p>
      <p>Please try again later.</p>
    `;
  },
  
  // Handle JSONP responses
  js(err, ctx) {
    const callback = ctx.query.callback || 'callback';
    ctx.body = `${callback}(${JSON.stringify({ error: err.message })})`;
  },
  
  // Redirect instead of showing error
  redirect: '/error-page',
  
  // Universal handler (overrides content negotiation)
  all(err, ctx) {
    // Log error internally
    console.error('Application Error:', err);
    
    // Send simple response regardless of Accept header
    ctx.status = err.status || 500;
    ctx.body = 'An unexpected error occurred';
  },
  
  // Custom content negotiation
  accepts(...types) {
    // Custom logic to determine response type
    return this.get('User-Agent').includes('mobile') ? 'json' : 'html';
  }
});

Error Handling Features

Automatic Error Processing

  • Non-Error Wrapping: Automatically wraps non-Error objects (strings, objects) in proper Error instances
  • Status Code Assignment: Sets appropriate HTTP status codes (404 for ENOENT, 500 for others)
  • Header Management: Propagates error headers to the response and manages header state
  • Stream Cleanup: Uses stream-wormhole to properly dispose of request streams during errors

Content Negotiation

The middleware automatically determines the response format based on the client's Accept header:

  • text/html → HTML error page (using built-in templates)
  • application/json → JSON error response
  • text/plain → Plain text error message
  • application/javascript → Custom JavaScript/JSONP handling (if js handler provided)

Development vs Production Modes

Error exposure is controlled by the NODE_ENV environment variable:

  • Development: Shows full error messages and stack traces
  • Production: Shows generic HTTP status messages for security

Built-in Templates

The package includes two HTML error templates:

  • Development Template: Shows detailed error information with stack traces
  • Production Template: Shows minimal error information for security

Error Object Enhancement

When errors occur, the middleware enhances them with additional properties:

// Original error
const err = new Error("Database connection failed");

// Enhanced by koa-onerror
err.status = 500;
err.headers = { 'X-Error-Code': 'DB_CONN_FAIL' };
err.expose = false; // Don't expose in production

// The enhanced error is then processed by the appropriate handler

Integration with Koa

The middleware integrates seamlessly with Koa's error handling system:

  1. Event Emission: All errors are emitted on the app instance for logging
  2. Context Integration: Replaces ctx.onerror method on all contexts
  3. Response Management: Handles response completion and header management
  4. Stream Management: Automatically handles request stream cleanup

Common Use Cases

API Error Responses

onerror(app, {
  json(err, ctx) {
    ctx.body = {
      success: false,
      error: {
        code: err.code || 'UNKNOWN_ERROR',
        message: err.message,
        status: err.status || 500
      },
      requestId: ctx.state.requestId
    };
  }
});

Custom Error Pages

onerror(app, {
  html(err, ctx) {
    const errorPage = getErrorTemplate(err.status);
    ctx.body = errorPage.replace('{{message}}', err.message);
  },
  redirect: null // Don't redirect, use custom template
});

Logging Integration

onerror(app, {
  all(err, ctx) {
    // Log error with context
    logger.error('Request error', {
      error: err.message,
      stack: err.stack,
      url: ctx.url,
      method: ctx.method,
      userAgent: ctx.get('User-Agent'),
      ip: ctx.ip
    });
    
    // Then handle normally based on content type
    const type = ctx.accepts('html', 'json', 'text');
    if (type === 'json') {
      ctx.body = { error: 'Internal server error' };
    } else {
      ctx.body = 'An error occurred';
    }
  }
});