Validations and Transformations for Backend Engineers

So far in this series, we've talked about HTTP, routing, serialization, authentication, and how requests eventually reach our backend.
But there's one assumption we've quietly been making:
The data coming from the client is correct.
And that's definitely not something a backend should assume.
Imagine our API expects:
{
"name": "Obi-Wan Kenobi",
"age": 38
}
but receives:
{
"name": 66,
"age": "hello there"
}
That's still perfectly valid JSON. The server can receive it. The JSON parser can deserialize it. But from our application's perspective, the data makes absolutely no sense.
That's where validation and transformation come into play.
What is Validation?
Validation is the process of checking whether incoming data follows the rules our application expects.
If any of these conditions fail, the request should ideally be rejected before it reaches our business logic or database.
This applies to almost anything coming from a client:
Request body
Query parameters
Path parameters
Headers
Cookies
Webhook payloads
If it's coming from outside your application, treat it as untrusted.
What is Transformation?
Validation asks:
Is this data acceptable?
Transformation asks:
Can I convert this data into the format my application expects?
Consider pagination:
GET /api/orders?page=2&limit=20
The values in the URL arrive as textual data, so your application may receive something equivalent to:
page = "2"
limit = "20"
But your pagination logic wants numbers:
page = 2
limit = 20
So we transform them.
Transformation can also be used for normalization:
" leia@example.com "
↓
Trim
↓
"leia@example.com"
So the flow often becomes:
Raw Input
↓
Transform
↓
Validate
↓
Application
The exact order can vary depending on the use case.
Where Does Validation Happen?
Let's connect this to the backend architecture we've been using throughout this series.
A simplified application might look like:
Request
↓
Router
↓
Controller
↓
Service
↓
Repository
↓
Database
Request validation usually happens near the entry point of the application, before untrusted input reaches the service layer.
Depending on the framework, this might be implemented through:
Middleware
Schema validators
DTOs
Request parsers
Controller validation
Conceptually:
Route Matched
↓
Request Data
↓
Validation / Transformation
↓
Controller
↓
Service
↓
Repository
The important part isn't exactly which file contains the validator.
The important part is:
Validate external input before allowing it into your business logic.
Why Backend Validation Matters
Suppose our database expects:
name → TEXT
but the client sends:
{
"name": 66
}
Without validation, this data might travel through the controller, service, and repository before eventually being rejected by the database or database driver.
If that error isn't handled properly, the client could receive:
500 Internal Server Error
But the server isn't really the problem.
The client sent invalid data.
Instead, we want:
Client
↓
Validation
↓
Invalid Input
↓
400 Bad Request
or, depending on your API conventions:
422 Unprocessable Content
The request stops there.
No unnecessary database call.
No business logic executed with invalid input.
Much cleaner.
The Validation Pipeline
A simple validation pipeline usually checks data in stages.
1. Does the value exist?
Suppose username is required.
{}
We can immediately reject the request.
2. Is it the correct type?
{
"username": true
}
The field exists, but we expected a string.
Reject it.
3. Does it satisfy our constraints?
{
"username": "a"
}
It's a string, but maybe usernames must contain at least three characters.
So the mental model becomes:
Exists?
Correct Type?
Correct Format?
Meets Constraints?
Accepted
The Main Types of Validation
There are four useful categories to keep in mind.
1. Type Validation
This checks whether the value has the expected data type.
For example:
{
"name": "Leia",
"age": 19,
"active": true
}
might require:
name → string
age → number
active → boolean
If age comes in as:
{
"age": "nineteen"
}
it fails.
2. Syntactic Validation
Sometimes the type is correct, but the format isn't.
For example:
{
"email": "definitely-not-an-email"
}
It's technically a string.
But it doesn't match the structure expected for an email address.
Other common examples include:
Dates
UUIDs
Phone numbers
URLs
IP addresses
So syntactic validation asks:
Does this value follow the expected structure?
3. Semantic Validation
This checks whether the data actually makes sense.
For example:
{
"dateOfBirth": "2099-01-01"
}
The value may be:
String? ✅
Correct date format? ✅
But the user hasn't been born yet.
That's semantically invalid.
Similarly:
{
"age": 430
}
might technically pass our type checks.
Semantic validation deals with logical meaning.
4. Dependent Validation
Sometimes the validity of one field depends on another.
For example:
{
"password": "helloThere123",
"confirmPassword": "helloThere123"
}
Both values might individually be valid strings.
But we also need:
password === confirmPassword
Another example could be:
If married = true
partnerName must exist
This is also called cross-field validation.
Transformation in Practice
Let's return to our pagination example:
GET /api/jedi?page=2&limit=20
Our application might receive:
page = "2"
limit = "20"
So we transform them:
"2" → 2
"20" → 20
Then validate:
Is integer?
Greater than 0?
Within allowed limit?
This gives us:
Raw Input
Transform
Validate
Normalized Data
Transformation can also remove unnecessary whitespace or normalize values where doing so is intentional.
But there's an important rule here:
Don't silently modify arbitrary user data.
For example, transforming an email may be acceptable depending on your system.
Transforming someone's password absolutely is not.
Frontend vs Backend Validation
A very common question is:
If my frontend already validates everything, why validate it again?
Because the frontend is not a security boundary.
Imagine your React form only accepts ages between 18 and 100.
Great.
But someone can completely ignore your React app and send:
POST /api/users
directly using:
Postman
Insomnia
curlCustom scripts
with:
{
"age": -500
}
Your frontend validation never gets involved.
So the distinction is simple:
Frontend Validation
Better UX
Backend Validation
Security + Data Integrity
Frontend validation helps legitimate users.
Backend validation protects the application.
You usually want both.
Request Validation vs Business Validation
There's one final distinction worth understanding.
Suppose the client sends:
{
"accountId": 123,
"amount": 100
}
Request validation can verify:
accountId → integer ✅
amount → number ✅
amount > 0 ✅
Everything looks fine.
But what if account 123 only contains ₹50?
Now the request is structurally valid, but the operation violates a business rule.
That check belongs deeper inside the application:
Request Validation
Service Layer
Does account have enough balance?
Yes / No
This gives us multiple layers of protection:
Request Validation
→ Is the input shaped correctly?
Business Validation
→ Is this operation logically allowed?
Database Constraints
→ Can invalid state still be prevented?
Your database might still enforce constraints such as:
NOT NULLUNIQUEFOREIGN KEYCHECK
Each layer protects something different.
Putting It All Together
A typical request might flow like this:
Incoming Request
↓
Route Matched
↓
Parse Input
↓
Transform
↓
Validate
↓
Controller
↓
Service
↓
Repository
↓
Database
If validation fails:
Invalid Input
↓
400 / 422
If it succeeds:
Valid Input
↓
Business Logic
That's basically the whole idea.
Validation ensures that the data entering our application satisfies the rules we expect.
Transformation converts that data into a representation our application can comfortably work with.
And the most important principle behind both is:
Never trust the client.
Because no matter how strict your frontend form is, someone somewhere has curl, Postman, and enough curiosity to find out what happens when they send:
{
"quantity": "execute order 66"
}
Your backend should already know the answer.
Thanks for reading.



