Input Validations and API Contracts
The 3-Line Engineering Rule
Validate all untrusted input at the network perimeter; fail fast with descriptive 400 Bad Request envelopes before touching domain logic.
Missing input validation allows SQL injection, payload-induced denial of service (ReDoS), corrupted databases, and security exploits.
Enforce strict schema validation (Jakarta Bean Validation / Zod), sanitize strings, bound list sizes, and reject unexpected JSON fields.
Architectural Blueprint & Sequence
Hover over nodes to inspect state transitionsExternal Client
POST /v1/users
Perimeter Validator
Schema, length, regex checks
400 Bad Request
RFC 7807 field validation errors
Domain Logic
Safe sanitized business entities
Clean Database
Valid, consistent data records
Key Engineering Takeaways
- Validate at the entry boundary; never let malformed data penetrate the service layer.
- Always set maximum string lengths and maximum collection sizes to prevent memory DoS.
- Return RFC 7807 structured field-level error messages so clients can fix bad input easily.
- Treat internal microservice payloads with the same zero-trust validation as public APIs.
The Problem & Production Reality
Input validation is the first line of defense for every software application. Clean code dictates that internal domain services should never have to ask: 'Is this string null? Is this array 10 million items long?' Validate strictly at the controller boundary and let the domain operate on trusted models.
Anti-Pattern: Ad-Hoc Validation Scattered Across Services
Checking `if (email == null)` deep inside the business service after executing multiple database lookups.
// ❌ ANTI-PATTERN: Late validation wastes resources and leaks edge cases
@PostMapping("/register")
public User register(@RequestBody UserRegistrationDto dto) {
// Controller accepts raw input without annotation
return userService.register(dto); // Service does ad-hoc checks 100 lines deep
}Production Pattern: Declarative Schema Validation with Boundary Limits
Use declarative annotations with strict length limits, email formats, and number bounds, combined with a global validation exception handler.
// ✅ PRODUCTION PATTERN: Declarative Boundary Validation
public record CreateUserRequest(
@NotBlank(message = "Username is required")
@Size(min = 3, max = 30, message = "Username must be between 3 and 30 characters")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Username must be alphanumeric")
String username,
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
@Size(max = 255)
String email,
@NotNull(message = "Age is required")
@Min(value = 18, message = "Must be at least 18 years old")
@Max(value = 120, message = "Age exceeds maximum allowable limit")
Integer age,
@NotEmpty(message = "At least one role must be specified")
@Size(max = 5, message = "Cannot assign more than 5 roles")
List<String> roles
) {}
@RestController
@RequestMapping("/v1/users")
public class UserController {
@PostMapping
public ResponseEntity<UserResponse> createUser(@Valid @RequestBody CreateUserRequest request) {
// Enters here ONLY if all boundary validations pass
return ResponseEntity.status(HttpStatus.CREATED).body(userService.create(request));
}
}Trade-off Analysis & Staff Recommendations
| Aspect | Pros / Strengths | Cons / Hazards | Staff Recommendation |
|---|---|---|---|
| Strict vs Permissive Validation | Strict validation eliminates injection vectors and malformed states. | Overly restrictive regex can reject valid international phone numbers or emails. | Use standard validation libraries (e.g. validator.js / Hibernate Validator) rather than custom regexes. |
| Reject Unknown Properties | Prevents mass assignment vulnerabilities where attackers inject `isAdmin: true`. | Breaks backward compatibility if an API client sends newer optional fields. | Enable `FAIL_ON_UNKNOWN_PROPERTIES` on public mutation APIs. |