Error handling shouldn’t feel like an afterthought—yet here we are, dealing with APIs that return errors in every format imaginable. Let’s fix that once and for all!
Introduction
In Part 1, we established a global error format for our microservices using http-error-kit and @wthek/express-middleware. While a global format ensures consistency, different error types may require unique structures based on their nature.
For example:
-
Validation Errors → Require field-specific details.
-
Authentication Errors → Should indicate re-authentication requirements.
-
Database Errors → May need query debugging information.
Instead of applying a one-size-fits-all approach, we will create custom error classes with instance-level formatters to maintain API-wide consistency while allowing flexibility.
Why Instance-Level Formatting Matters
"Perfectly structured, as all things should be." – Adapted from Thanos, Avengers: Infinity War.
A global error format is great for high-level consistency, but different error types should have structured variations.
Consider this example:
Scenario: API Without Instance-Level Formatting
{
"errorCode": 400,
"errorMessage": "Validation failed",
"errorDetails": { "field": "email" },
"timestamp": "2025-03-10T12:00:00.000Z"
}
This structure is generic, but what if the API consumer needs field-specific errors for better UI feedback?
Instead, we should allow different formats for different error classes while keeping an API-wide structure for common error handling.
Step 1: Creating Custom Error Classes with Instance-Level Formatting
Think of instance-level formatting as giving each error type its own uniform—validation errors, auth failures, and server issues all dressed in a structured, predictable format.
Instead of using the global formatter, we can extend http-error-kit
to define custom error types with their own formats.
Defining a Custom Validation Error Class
import { KitHttpError } from "http-error-kit";
import { BAD_REQUEST } from 'http-response-status-code';
// Define the instance-level formatter
const formatter = (statusCode, message, details, ...args) => ({
type: "VALIDATION_ERROR",
status: statusCode,
message,
fields: details,
timestamp: new Date().toISOString(),
});
class ValidationError extends KitHttpError {
constructor(message: string, fieldErrors: Record<string, string>, ...args) {
super(BAD_REQUEST, message, fieldErrors, ...args);
this.setFormatter(formatter); // Apply custom formatting
}
}
Now, when we throw a ValidationError, it follows a custom format:
throw new ValidationError("Invalid Input", { email: "Email is required" });
Response Output:
{
"type": "VALIDATION_ERROR",
"status": 400,
"message": "Invalid Input",
"fields": { "email": "Email is required" },
"timestamp": "2025-03-10T12:00:00.000Z"
}
Step 2: Using @wthek/express-middleware to Handle Custom Errors
Now, we integrate the custom error classes into our Express microservice:
import express from "express";
import { KitExpressMiddleware } from "@wthek/express-middleware";
import { ValidationError } from "./errors/ValidationError";
const app = express();
// Example Route
app.post("/register", (req, res, next) => {
const email = req.body.email;
if (!email) {
next(new ValidationError("Invalid Input", { email: "Email is required" }));
}
});
// Error Handling Middleware
app.use(KitExpressMiddleware());
app.listen(3000, () => console.log("Server running on port 3000"));
Response Output (When Email is Missing):
{
"type": "VALIDATION_ERROR",
"status": 400,
"message": "Invalid Input",
"fields": { "email": "Email is required" },
"timestamp": "2025-03-10T12:00:00.000Z"
}
Summary
In Part 1, we set up a global error format for API-wide consistency.
In Part 2, we added custom error classes with instance-level formatters, ensuring:
-
API-wide consistency for error handling.
-
Custom formatting per error type.
-
Cleaner and structured error responses.
A standardized error strategy doesn’t just help your team—it makes your API a delight to integrate with. Developers love consistency, and your API should deliver exactly that.
This approach keeps public APIs predictable while allowing specific error formats when necessary.
🚀 Now, your microservice has a fully customizable error handling system!
Tidak ada komentar:
Posting Komentar