Error Handling & Logging
Relevant source files
The following files were used as context for generating this wiki page:
The Subito project employs a centralized error handling and logging architecture designed to provide consistent API responses and comprehensive request tracing. The system utilizes structured logging to capture request metadata, while a specialized error hierarchy ensures that operational failures are translated into actionable HTTP responses.
This system is integrated directly into the Hono-based API server via middleware, ensuring that every request—from entry to exit—is monitored and that any exceptions thrown during business logic execution, such as in the Auth Service, are caught and formatted uniformly.
Error Architecture
The error handling system is built upon a custom AppError class that extends the native JavaScript Error. This base class introduces properties for HTTP status codes, internal error codes, and additional diagnostic details.
Error Hierarchy
Subito defines a range of specialized error classes that correspond to specific HTTP status codes and application scenarios. This allows developers to throw descriptive errors that the global middleware automatically recognizes.
| Error Class | Status Code | Internal Code | Use Case |
|---|---|---|---|
AppError | Variable | Variable | Base class for all application errors |
BadRequestError | 400 | BAD_REQUEST | General client-side input errors |
UnauthorizedError | 401 | UNAUTHORIZED | Authentication failures or missing tokens |
ForbiddenError | 403 | FORBIDDEN | Authorization failures (insufficient permissions) |
NotFoundError | 404 | NOT_FOUND | Missing resources or invalid routes |
ConflictError | 409 | CONFLICT | Resource state conflicts (e.g., duplicate phone) |
ValidationError | 422 | VALIDATION_ERROR | Data validation failures (Zod, etc.) |
TooManyRequestsError | 429 | TOO_MANY_REQUESTS | Rate limit or brute-force prevention |
InternalError | 500 | INTERNAL_ERROR | Unexpected server-side failures |
ServiceUnavailableError | 503 | SERVICE_UNAVAILABLE | External service or dependency failures |
Sources: apps/api/src/lib/errors.ts:1-62
Global Error Middleware
The errorHandler middleware serves as the final catch-point for the API. It intercepts all thrown errors, logs them using the application logger, and returns a structured JSON response to the client.
The middleware adjusts the level of detail returned based on the environment; for instance, stack traces are excluded from responses in production environments.
Sources: apps/api/src/middleware/errror-handler.ts:7-45, apps/api/src/app.ts:89
Structured Logging
Logging is implemented using the pino library, providing high-performance, structured JSON logs. The logging strategy focuses on request tracing and client metadata identification.
Request Tracing and Metrics
The application uses hono middleware to inject a requestId and track request duration. This information is logged for every request regardless of success or failure.
- Request ID: Generated via
requestId()middleware to correlate logs for a single transaction. - Timing: Captured via
timing()middleware to measure performance. - Client Metadata: The
clientHeadersLoggercaptures specific headers such asX-Platform,X-App-Version, and a prefix of theX-Device-ID.
Sources: apps/api/src/app.ts:51-87, apps/api/package.json:28
Logging Context
The following diagram illustrates how request context is gathered and logged during the request lifecycle.
Database Error Tracking
The database schema includes specific tables to track idempotent requests and failed payment/refund operations, which aids in debugging transactional failures.
- Idempotency Keys: The
idempotency_keystable stores theresponse_codeandresponse_bodyfor specific requests, preventing duplicate processing of retried operations. - Payment Failures: The
paymentstable containserror_codeanderror_messagefields to capture gateway-level rejections. - Refund Failures: The
refundstable includes afailure_reasonfield.
Sources: packages/db/drizzle/0000_normal_the_order.sql:357-368, packages/db/drizzle/0000_normal_the_order.sql:403-410, packages/db/drizzle/0000_normal_the_order.sql:425-427
Implementation in Services
Services, such as the AuthService, utilize the custom error classes to enforce business rules. For example, the login function throws a TooManyRequestsError if OTP limits are exceeded, which is then caught by the global error handler.
// Example from Auth Service
if (!send.allowed) {
throw new TooManyRequestsError(
`Too many OTP requests. Try again in ${Math.ceil(send.retryAfterSec / 60)} minutes.`,
{ retryAfterSec: send.retryAfterSec }
)
}
Sources: apps/api/src/modules/auth/auth.service.ts:114-120
Conclusion
The Subito project implements a robust error handling and logging framework that ensures system reliability and maintainability. By combining structured PINO logs, unique request IDs, and a strictly typed error hierarchy, the architecture provides developers with the necessary tools to trace issues effectively across the API and database layers.