Skip to main content
The ErrorResponse class extends the native Error class to provide structured, type-safe error responses with HTTP status codes and custom error data.

ErrorResponse.Base

A structured error response class that includes status information, metadata, and custom output.
import { ErrorResponse } from '@apisr/response';

const error = new ErrorResponse.Base({
  name: 'ValidationError',
  meta: { requestId: 'req-123' },
  output: { message: 'Invalid input', field: 'email' },
  status: 422,
  statusText: 'Unprocessable Entity'
});

Constructor Parameters

name
TName
required
Error name identifier (e.g., “notFound”, “unauthorized”, “validationError”)
meta
TMeta
required
Metadata object containing contextual information (e.g., requestId, timestamp)
output
TOutput
required
Error output object with error details (message, code, additional data)
status
number
required
HTTP status code (e.g., 404, 500)
statusText
string
required
HTTP status text (e.g., “Not Found”, “Internal Server Error”)

Properties

name
TName
Error name identifier
meta
TMeta
Metadata associated with the error
output
TOutput
Structured error output data
status
number
HTTP status code
statusText
string
HTTP status text

Built-in Error Types

The response handler includes 7 built-in error types that can be used immediately:

unauthorized

401 Unauthorized - User is not authenticated
const response = handler.fail('unauthorized');
// Status: 401
// Output: { message: 'Unauthorized', code: 'UNAUTHORIZED', name: 'UnauthorizedError' }

forbidden

403 Forbidden - User is authenticated but lacks permissions
const response = handler.fail('forbidden');
// Status: 403
// Output: { message: 'Forbidden', code: 'FORBIDDEN', name: 'ForbiddenError' }

notFound

404 Not Found - Requested resource does not exist
const response = handler.fail('notFound');
// Status: 404
// Output: { message: 'Not Found', code: 'NOT_FOUND', name: 'NotFoundError' }

badRequest

400 Bad Request - Invalid request parameters
const response = handler.fail('badRequest');
// Status: 400
// Output: { message: 'Bad Request', code: 'BAD_REQUEST', name: 'BadRequestError' }

conflict

409 Conflict - Request conflicts with current state
const response = handler.fail('conflict');
// Status: 409
// Output: { message: 'Conflict', code: 'CONFLICT', name: 'ConflictError' }

tooMany

429 Too Many Requests - Rate limit exceeded
const response = handler.fail('tooMany');
// Status: 429
// Output: { message: 'Too Many Requests', code: 'TOO_MANY_REQUESTS', name: 'TooManyRequestsError' }

internal

500 Internal Server Error - Unexpected server error
const response = handler.fail('internal');
// Status: 500
// Output: { message: 'Internal Server Error', code: 'INTERNAL_SERVER_ERROR', name: 'InternalServerError' }

Default Error Structure

All built-in errors follow this structure:
interface DefaultError {
  message: string;
  code: string;
  name: string;
  cause?: unknown;
  stack?: string[];
}

Defining Custom Errors

Use defineError() to create custom error types with validation:
import { createResponseHandler } from '@apisr/response';
import { z } from '@apisr/zod';

const handler = createResponseHandler({}).defineError(
  'validationError',
  ({ input, meta }) => ({
    message: `Validation failed: ${input.message}`,
    code: 'VALIDATION_ERROR',
    field: input.field,
    requestId: meta.requestId
  }),
  {
    status: 422,
    statusText: 'Unprocessable Entity',
    input: z.object({
      field: z.string(),
      message: z.string()
    })
  }
);

// Use the custom error
const response = handler.fail('validationError', {
  field: 'email',
  message: 'Invalid email format'
});

Error with Cause

Include the original error as a cause:
export const GET = async () => {
  try {
    const data = await fetchData();
    return handler.json(data);
  } catch (err) {
    return handler.fail('internal', { cause: err });
  }
};

Custom Error Mapping

Transform errors globally using mapDefaultError:
import { createResponseHandler, options } from '@apisr/response';

const handler = createResponseHandler((opts) => ({
  error: opts.error({
    mapDefaultError: (error) => ({
      ...error,
      // Add additional fields
      timestamp: Date.now(),
      environment: process.env.NODE_ENV
    })
  })
}));

Error Handler Options

When defining custom errors, you can specify:
status
number
HTTP status code for the error
statusText
string
HTTP status text for the error
input
Schema
Zod schema for validating error input
validationType
'parse' | 'assert'
Validation type (default: “parse”)
  • parse - Validate and transform input
  • assert - Validate input and throw if invalid

Static Error Output

Define errors with static output (no handler function):
const handler = createResponseHandler({}).defineError(
  'maintenance',
  {
    message: 'Service is under maintenance',
    code: 'MAINTENANCE',
    name: 'MaintenanceError'
  },
  {
    status: 503,
    statusText: 'Service Unavailable'
  }
);

const response = handler.fail('maintenance');

Real-World Example

import { createResponseHandler } from '@apisr/response';
import { z } from '@apisr/zod';

const handler = createResponseHandler({})
  .defineError(
    'validationError',
    ({ input }) => ({
      message: 'Validation failed',
      code: 'VALIDATION_ERROR',
      errors: input.errors
    }),
    {
      status: 422,
      input: z.object({
        errors: z.array(z.object({
          field: z.string(),
          message: z.string()
        }))
      })
    }
  )
  .defineError(
    'rateLimitExceeded',
    ({ input, meta }) => ({
      message: 'Rate limit exceeded',
      code: 'RATE_LIMIT_EXCEEDED',
      retryAfter: input.retryAfter,
      requestId: meta.requestId
    }),
    {
      status: 429,
      input: z.object({
        retryAfter: z.number()
      })
    }
  );

export const POST = async (request: Request) => {
  const body = await request.json();
  
  // Validation
  const errors = validateInput(body);
  if (errors.length > 0) {
    return handler.fail('validationError', { errors });
  }
  
  // Rate limiting
  const remaining = await checkRateLimit(request);
  if (remaining === 0) {
    return handler.fail('rateLimitExceeded', { retryAfter: 60 });
  }
  
  // Success
  const result = await processData(body);
  return handler.json(result);
};

Build docs developers (and LLMs) love