Scenario and definition
A Food Delivery Platform's order-creation endpoint must validate that a delivery address is non-empty, an order contains at least one item, and item quantities are positive integers — rejecting malformed requests with a clear, field-level error response before any business logic runs. Node.js (NestJS) achieves this through class-validator decorators on DTO classes combined with a global validation pipe. Spring Boot achieves this through Jakarta Bean Validation annotations combined with @Valid.
Keywords
Code comparison
export class OrderItemDto {
@IsString() @IsNotEmpty()
menuItemId: string;
@IsInt() @Min(1)
quantity: number;
}
export class CreateOrderDto {
@IsString() @IsNotEmpty()
deliveryAddress: string;
@IsArray() @ArrayMinSize(1)
@ValidateNested({ each: true })
@Type(() => OrderItemDto)
items: OrderItemDto[];
}public class OrderItemRequest {
@NotBlank
private String menuItemId;
@Min(1)
private int quantity;
}
public class CreateOrderRequest {
@NotBlank
private String deliveryAddress;
@NotEmpty
@Valid
private List<OrderItemRequest> items;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
return ResponseEntity.badRequest().body(errors);
}Code explanation
node
class-validator decorators attach metadata to DTO class properties; the global ValidationPipe reads this metadata at runtime and rejects requests failing any constraint before the controller method body ever executes. @ValidateNested({ each: true }) combined with @Type(() => OrderItemDto) is necessary because incoming JSON deserializes into plain objects by default — without @Type, class-validator wouldn't know to validate each array item as an OrderItemDto instance.
springboot
Jakarta Bean Validation annotations are a Java EE/Jakarta EE standard, not Spring-specific. @Valid on the nested items list triggers cascading validation into each OrderItemRequest automatically — Spring's validation cascades into nested objects natively without an equivalent to NestJS's @Type workaround, since Java's strong runtime typing already knows the list's generic type. The explicit @ExceptionHandler is required to produce a custom field-level error map.
Backend integration
node
Validation happens entirely before OrdersService.create() is invoked, guaranteeing the service layer can trust dto's shape is already satisfied, simplifying business logic.
springboot
Identically, orderService.create() only ever receives a request object that's already passed all @Valid constraint checks, since validation runs during the @RequestBody deserialization step before the controller method body executes.
Interview questions
Why is @Type(() => OrderItemDto) required for nested array validation in NestJS, but Spring's @Valid on a List field needs no equivalent annotation?
When NestJS deserializes incoming JSON, the result is plain JavaScript objects with no class identity — without @Type telling class-transformer to instantiate each array element as an OrderItemDto, the nested objects remain plain objects and their decorators are silently never checked. Java's JSON deserialization uses reflection against the DECLARED generic type of the List field, so Spring already knows the concrete type without an additional hint annotation.
Why does Spring require an explicit @ExceptionHandler to produce field-level error messages, while NestJS provides this structure by default?
Spring's default validation failure response is generally considered too noisy for production APIs, so teams almost universally add a custom @ExceptionHandler to reshape it into a clean field-to-message map. NestJS's built-in ValidationPipe was designed with a more opinionated, ready-to-use default error format out of the box, reflecting NestJS's generally more batteries-included philosophy for common REST API concerns.