# 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:

```json
{
  "name": "Obi-Wan Kenobi",
  "age": 38
}
```

but receives:

```json
{
  "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.

![](https://cdn.hashnode.com/uploads/covers/611d5567a285e422d5a131ac/f39b493b-9956-4bcc-a0e0-fffc13777bf7.png align="center")

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:

```http
GET /api/orders?page=2&limit=20
```

The values in the URL arrive as textual data, so your application may receive something equivalent to:

```text
page = "2"
limit = "20"
```

But your pagination logic wants numbers:

```text
page = 2
limit = 20
```

So we transform them.

Transformation can also be used for normalization:

```text
"  leia@example.com  "
        ↓
Trim
        ↓
"leia@example.com"
```

So the flow often becomes:

```text
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:

```text
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:

```text
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:

```text
name → TEXT
```

but the client sends:

```json
{
  "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:

```http
500 Internal Server Error
```

But the server isn't really the problem.

The client sent invalid data.

Instead, we want:

```text
Client
  ↓
Validation
  ↓
Invalid Input
  ↓
400 Bad Request
```

or, depending on your API conventions:

```http
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.

```json
{}
```

We can immediately reject the request.

### 2\. Is it the correct type?

```json
{
  "username": true
}
```

The field exists, but we expected a string.

Reject it.

### 3\. Does it satisfy our constraints?

```json
{
  "username": "a"
}
```

It's a string, but maybe usernames must contain at least three characters.

So the mental model becomes:

```text
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:

```json
{
  "name": "Leia",
  "age": 19,
  "active": true
}
```

might require:

```text
name   → string
age    → number
active → boolean
```

If `age` comes in as:

```json
{
  "age": "nineteen"
}
```

it fails.

* * *

### 2\. Syntactic Validation

Sometimes the type is correct, but the format isn't.

For example:

```json
{
  "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:

```json
{
  "dateOfBirth": "2099-01-01"
}
```

The value may be:

```text
String? ✅
Correct date format? ✅
```

But the user hasn't been born yet.

That's semantically invalid.

Similarly:

```json
{
  "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:

```json
{
  "password": "helloThere123",
  "confirmPassword": "helloThere123"
}
```

Both values might individually be valid strings.

But we also need:

```text
password === confirmPassword
```

Another example could be:

```text
If married = true
partnerName must exist
```

This is also called **cross-field validation**.

![](https://cdn.hashnode.com/uploads/covers/611d5567a285e422d5a131ac/f83ed043-8ba9-47bf-8ac2-8fd2b12ad79d.png align="center")

* * *

## Transformation in Practice

Let's return to our pagination example:

```http
GET /api/jedi?page=2&limit=20
```

Our application might receive:

```text
page = "2"
limit = "20"
```

So we transform them:

```text
"2"  → 2
"20" → 20
```

Then validate:

```text
Is integer?
     
Greater than 0?
     
Within allowed limit?
```

This gives us:

```text
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.

![](https://cdn.hashnode.com/uploads/covers/611d5567a285e422d5a131ac/181d8200-5b29-49f5-98b9-14e206254e1a.png align="center")

* * *

## 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:

```http
POST /api/users
```

directly using:

*   Postman
    
*   Insomnia
    
*   `curl`
    
*   Custom scripts
    

with:

```json
{
  "age": -500
}
```

Your frontend validation never gets involved.

So the distinction is simple:

```text
Frontend Validation
        
Better UX

Backend Validation
        
Security + Data Integrity
```

Frontend validation helps legitimate users.

Backend validation protects the application.

You usually want both.

![](https://cdn.hashnode.com/uploads/covers/611d5567a285e422d5a131ac/80c0d2d7-e4eb-4878-8803-f71d25a75541.png align="center")

* * *

## Request Validation vs Business Validation

There's one final distinction worth understanding.

Suppose the client sends:

```json
{
  "accountId": 123,
  "amount": 100
}
```

Request validation can verify:

```text
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:

```text
Request Validation
        
Service Layer
        
Does account have enough balance?
        
Yes / No
```

This gives us multiple layers of protection:

```text
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 NULL`
    
*   `UNIQUE`
    
*   `FOREIGN KEY`
    
*   `CHECK`
    

Each layer protects something different.

* * *

## Putting It All Together

A typical request might flow like this:

![]( align="center")

```text
Incoming Request
      ↓
Route Matched
      ↓
Parse Input
      ↓
Transform
      ↓
Validate
      ↓
Controller
      ↓
Service
      ↓
Repository
      ↓
Database
```

If validation fails:

```text
Invalid Input
     ↓
400 / 422
```

If it succeeds:

```text
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:

```json
{
  "quantity": "execute order 66"
}
```

Your backend should already know the answer.

Thanks for reading.
