Skip to content
nooh
Esc
↑↓navigate↵open⌘Jpreview
On this page

Errors

Defining custom domain errors, error hierarchy, and type-safe error propagation.

Nooh promotes standard JavaScript and TypeScript classes for domain errors. Custom errors help you distinguish expected business rule failures from unexpected infrastructure crashes.

Creating Custom Errors

Extend JavaScript’s built-in Error class to define application-specific errors:

export class UserNotFoundError extends Error {
  readonly status = 404;
  readonly code = "USER_NOT_FOUND";

  constructor(public readonly userId: string) {
    super(`User with ID "${userId}" was not found.`);
    this.name = "UserNotFoundError";
  }
}

export class DuplicateEmailError extends Error {
  readonly status = 409;
  readonly code = "DUPLICATE_EMAIL";

  constructor(public readonly email: string) {
    super(`Email "${email}" is already registered.`);
    this.name = "DuplicateEmailError";
  }
}

Declaring and Throwing Errors in Handlers

You can declare domain errors in your route endpoint using the errors option. Nooh generates type-safe error factory wrappers and passes them to your handler in the errors property of the input object:

import { get } from "@router/users/[id]";
import { services } from "@/dependencies";
import { UserNotFoundError } from "@/errors/user";

export default get({
  deps: [services.userService],
  errors: {
    UserNotFound: UserNotFoundError,
  },
  handler: async ({ c, userService, errors }) => {
    const { id } = c.req.param();
    const user = await userService.findById(id);

    if (!user) {
      // errors.UserNotFound is an ErrorFactory typed to instantiate UserNotFoundError
      throw errors.UserNotFound(id);
    }

    return c.json(user);
  },
});

When thrown, the error bubbles up to the nearest error handler (endpoint onError, route group, or global onError), where it can be formatted into a consistent JSON response.

Error Context & Factories

Under the hood, Nooh uses ErrorConstructor, ErrorFactory, and ErrorContext types to wrap error constructors into type-safe factory functions:

import type { ErrorConstructor, ErrorFactory, ErrorContext } from "@nooh-ts/nooh";

export type AppErrors = {
  UserNotFound: typeof UserNotFoundError;
  DuplicateEmail: typeof DuplicateEmailError;
};

// ErrorContext<AppErrors> provides typed factory helpers:
// errors.UserNotFound(userId: string) => UserNotFoundError

Was this page helpful?