My Journey of learning backend development
Today: Input Validation + Clean Errors
This is where your API stops being naïve and starts being safe.
Never trust client input. Ever. Here's what I learned
#LearnInPublic#100DaysOfCode
My API now has:
- Consistent response structure
- Global exception handling
- Input validation with clean errors
Follow along if you're on the same journey
#SpringBoot#Java#LearnInPublic
My Journey of learning backend development
Today: Input Validation + Clean Errors
This is where your API stops being naïve and starts being safe.
Never trust client input. Ever. Here's what I learned
#LearnInPublic#100DaysOfCode
Why validate at the DTO level, not inside the service?
Because the service shouldn't care about malformed input that's not its job. DTOs are the boundary between the outside world and your system. Keep concerns separated. One layer, one responsibility.
Common mistakes I learned to avoid:
- Mixing raw responses with ApiResponse
- Catching exceptions inside the service layer
- Returning stack traces to the frontend
Stack traces are for logs. Never for clients.
The fix: a universal ApiResponse<T> wrapper.
Every response , success or error — now looks the same:
{ "success": true, "data": { ... } }
{ "success": "false", "message": "User not found" }
Frontend devs can actually rely on this.
The Solutiion: @RestControllerAdvice + @ExceptionHandler annotation
One class catches ALL exceptions thrown anywhere in your app.
Service throws → Spring catches → handler returns clean JSON.
Controller stays clean. Service stays clean.
Why handle exceptions globally instead of in each controller?
Because duplicating try/catch everywhere is:
– Error-prone
– Hard to keep consistent
One handler = one source of truth. Change it once, and it works everywhere.
Common mistakes I learned to avoid:
- Mixing raw responses with ApiResponse
- Catching exceptions inside the service layer
- Returning stack traces to the frontend
Stack traces are for logs. Never for clients.
Why handle exceptions globally instead of in each controller?
Because duplicating try/catch everywhere is:
– Error-prone
– Hard to keep consistent
One handler = one source of truth. Change it once, and it works everywhere.
Why handle exceptions globally instead of in each controller?
Because duplicating try/catch everywhere is:
– Error-prone
– Hard to keep consistent
One handler = one source of truth. Change it once, and it works everywhere.
The Solutiion: @RestControllerAdvice + @ExceptionHandler annotation
One class catches ALL exceptions thrown anywhere in your app.
Service throws → Spring catches → handler returns clean JSON.
Controller stays clean. Service stays clean.
The Solutiion: @RestControllerAdvice + @ExceptionHandler annotation
One class catches ALL exceptions thrown anywhere in your app.
Service throws → Spring catches → handler returns clean JSON.
Controller stays clean. Service stays clean.
The fix: a universal ApiResponse<T> wrapper.
Every response , success or error — now looks the same:
{ "success": true, "data": { ... } }
{ "success": "false", "message": "User not found" }
Frontend devs can actually rely on this.