Overview
Format validation errors with field-level details. Includes custom ValidationException.
Features
- Field-level errors
- Custom ValidationException
- Helper functions
- class-validator support
Installation
npx hanma add validation-filter --framework nest
Code
import {
ExceptionFilter,
Catch,
ArgumentsHost,
BadRequestException,
} from "@nestjs/common";
import { Request, Response } from "express";
export interface ValidationErrorItem {
field: string;
message: string;
value?: any;
}
export interface ValidationErrorResponse {
statusCode: 400;
error: "Validation Error";
message: string;
errors: ValidationErrorItem[];
path: string;
timestamp: string;
}
/**
* Custom exception for validation errors with structured field errors.
*/
export class ValidationException extends BadRequestException {
constructor(public readonly errors: ValidationErrorItem[]) {
super({
statusCode: 400,
message: "Validation failed",
errors,
});
}
}
@Catch(BadRequestException)
export class ValidationExceptionFilter implements ExceptionFilter {
catch(exception: BadRequestException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const exceptionResponse = exception.getResponse() as any;
// Check if this is a validation error
const isValidationError =
exceptionResponse?.errors ||
(Array.isArray(exceptionResponse?.message) &&
exceptionResponse.message.length > 0);
if (!isValidationError) {
// Not a validation error, let it pass through
response.status(400).json({
statusCode: 400,
error: "Bad Request",
message: exceptionResponse?.message || exception.message,
path: request.url,
timestamp: new Date().toISOString(),
});
return;
}
const errors = this.extractErrors(exceptionResponse);
const errorResponse: ValidationErrorResponse = {
statusCode: 400,
error: "Validation Error",
message: `Validation failed: ${errors.length} error(s)`,
errors,
path: request.url,
timestamp: new Date().toISOString(),
};
response.status(400).json(errorResponse);
}
private extractErrors(response: any): ValidationErrorItem[] {
// Handle custom ValidationException
if (response.errors && Array.isArray(response.errors)) {
return response.errors;
}
// Handle class-validator errors (string array)
if (Array.isArray(response.message)) {
return response.message.map((msg: string) => {
const match = msg.match(/^(\w+)\s+(.+)$/);
if (match) {
return {
field: match[1],
message: match[2],
};
}
return {
field: "unknown",
message: msg,
};
});
}
// Fallback
return [
{
field: "unknown",
message: response.message || "Validation failed",
},
];
}
}
// Helper function to throw validation errors
export function throwValidationError(
field: string,
message: string,
value?: any
): never {
throw new ValidationException([{ field, message, value }]);
}
export function throwValidationErrors(errors: ValidationErrorItem[]): never {
throw new ValidationException(errors);
}
// Usage:
/*
import { ValidationException, throwValidationError } from "./filters/validation.filter";
@Injectable()
export class UserService {
async create(dto: CreateUserDto) {
// Custom validation
const existingUser = await this.findByEmail(dto.email);
if (existingUser) {
throwValidationError("email", "Email already exists", dto.email);
}
// Multiple errors
const errors: ValidationErrorItem[] = [];
if (dto.password === dto.email) {
errors.push({ field: "password", message: "Password cannot be same as email" });
}
if (errors.length > 0) {
throw new ValidationException(errors);
}
}
}
*/
Usage
throwValidationError('email', 'Email already exists');
Dependencies
No external dependencies required.
Frequently Asked Questions
How do I install Validation Filter?
Run the following command:
npx hanma add validation-filter --framework nestHow do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to
src/filters/validation.filter.ts.Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.