Skip to content

onError Handler

The handleError function is an error handler for Hono applications that converts Nimbus exceptions to structured HTTP JSON responses.

An in Depth Example

This guide also has an in depth example of a working application built with Nimbus. Combining DDD, CQRS and Event Sourcing.

Check out the In Depth Example page to learn how everything is connected and works out in a real-world application.

Basic Usage

typescript
import { Hono } from "hono";
import { handleError } from "@nimbus-cqrs/hono";

const app = new Hono();

// Register the error handler
app.onError(handleError);

Response Format

When a Nimbus exception is thrown, the handler returns a JSON response with the following structure:

json
{
    "error": "EXCEPTION_NAME",
    "message": "Human-readable error message",
    "details": { ... }
}
FieldDescription
errorThe exception name (e.g., NOT_FOUND, INVALID_INPUT)
messageThe error message provided when throwing the exception
detailsOptional additional details (only included if provided)

Status Code Mapping

The HTTP status code is taken directly from the exception's statusCode property:

ExceptionStatus CodeResponse error
GenericException500GENERIC_EXCEPTION
InvalidInputException400INVALID_INPUT
NotFoundException404NOT_FOUND
UnauthorizedException401UNAUTHORIZED
ForbiddenException403FORBIDDEN
Custom exceptions(custom)(custom name)

Logging Behavior

The handler logs errors differently based on the status code:

Status CodeLog LevelDescription
5xxerrorServer errors that need investigation
4xxdebugClient errors, typically expected
UnhandledcriticalNon-Nimbus errors, unexpected failures

Example: Exception Handling

typescript
import { Hono } from "hono";
import { handleError } from "@nimbus-cqrs/hono";
import { NotFoundException, InvalidInputException } from "@nimbus-cqrs/core";

const app = new Hono();

app.get("/users/:id", async (c) => {
    const user = await findUser(c.req.param("id"));

    if (!user) {
        throw new NotFoundException("User not found", {
            userId: c.req.param("id"),
        });
    }

    return c.json(user);
});

app.post("/users", async (c) => {
    const body = await c.req.json();

    if (!body.email) {
        throw new InvalidInputException("Email is required", {
            field: "email",
        });
    }

    const user = await createUser(body);
    return c.json(user, 201);
});

app.onError(handleError);

Response Examples

NotFoundException (404):

json
{
    "error": "NOT_FOUND",
    "message": "User not found",
    "details": {
        "userId": "123"
    }
}

InvalidInputException (400):

json
{
    "error": "INVALID_INPUT",
    "message": "Email is required",
    "details": {
        "field": "email"
    }
}

Unhandled Error (500):

json
{
    "error": "INTERNAL_SERVER_ERROR"
}

Complete Application Setup

typescript
import { Hono } from "hono";
import { correlationId, handleError, logger } from "@nimbus-cqrs/hono";
import { setupLogger, parseLogLevel } from "@nimbus-cqrs/core";

setupLogger({
    logLevel: parseLogLevel(process.env.LOG_LEVEL),
});

const app = new Hono();

app.use(correlationId());
app.use(logger({ enableTracing: true }));

// Your routes here
app.get("/health", (c) => c.json({ status: "ok" }));

// Error handler must be registered last
app.onError(handleError);

export default app;