← Back to list

Your Application Shouldn’t Trust the Data It Receives

Every application accepts data.

Abhimanyu · 2026-09-11 18:04 · 0 claps · 8.4 min read
#backend-development #api-security #validation #user-experience
Open on Medium ↗
Wiki topics: UX · UI/UX Design 🌐 · Web Development

Your Application Shouldn’t Trust the Data It Receives

Every application accepts data.

A name.

An email address.

A password.

A search query.

An order quantity.

A date.

Sometimes that data comes from a form. Sometimes it comes from another application. Sometimes it comes from an API client you’ve never seen before.

And here’s the uncomfortable part:

Your application shouldn’t automatically trust any of it.

Not because every user is malicious.

Simply because the application cannot assume that incoming data follows the rules it expects.

This is where validation and transformations become important.

I spent some time learning about these concepts today, and what initially looked like a simple topic turned into a much bigger part of application design.

The basic idea is:

Data comes in
      ↓
Is it acceptable?
      ↓
Validation
      ↓
Should its representation change?
      ↓
Transformation
      ↓
Business logic

The deeper technical breakdown is on my portfolio:

Read the full Validation & Transformations breakdown →

But here’s what I learned.

Validation Is More Than “Required”

Imagine you’re creating an account.

You enter:

Name: Vishal
Age: 24
Email: vishal@example.com

Everything looks fine.

But the application still needs to check the information before using it.

For example:

Is the name actually text?
Was the name provided?
Is the email written correctly?
Is the age a number?
Is the age within an acceptable range?

That’s validation.

In simple terms:

Validation is checking whether incoming information follows the rules the application expects.

And those rules aren’t all the same.

The First Question: Is It the Right Kind of Data?

Suppose an application expects:

Age → number

But it receives:

Age → "24"

The value looks reasonable to a human.

But technically, these are different:

24

and:

"24"

One is a number.

The other is text containing a number.

Whether the application should accept this depends on its contract.

This is type validation.

The question is:

“Did I receive the kind of value I expected?”

Then Comes Format

Suppose you receive:

vishal@example.com

The application can check whether it follows an expected email format.

Compare that with:

vishal@@example

The second value doesn’t follow the expected structure.

This is generally referred to as syntactic validation.

It asks:

“Does this information follow the expected format?”

This applies to many things:

Email addresses
Phone numbers
URLs
Dates
Usernames
IDs

But there’s an important limitation.

Something can look correct and still be wrong.

The Information Can Be Correctly Formatted and Still Make No Sense

Consider:

Age: -10

It’s a number.

So the type is correct.

There’s nothing particularly strange about the way the number is written.

But an age of -10 doesn't make sense under normal application rules.

Or imagine:

Start date: December 20
End date: December 10

Both dates could individually be valid.

But together they may violate the application’s rules.

This is semantic validation.

It asks:

“Does this information make sense in the context where we’re using it?”

That’s an important difference:

Syntactic
→ Is it correctly formatted?
Semantic
→ Does it make sense?

Think About an Online Store

Let’s say you’re buying a laptop.

Your request contains:

{
  "productId": "abc123",
  "quantity": 5
}

The application can validate:

productId → correct type
quantity → number
quantity → positive
quantity → within allowed range

But there’s another layer.

What if:

productId doesn't exist?

Or:

product exists
but is out of stock

Or:

the user isn't allowed to purchase it

These aren’t simply questions about whether the input is written correctly.

They’re questions about the rules of the application.

This is where validation starts touching business logic.

Validation Isn’t Transformation

Now imagine the user enters:

"   Vishal   "

The information may be perfectly acceptable.

But the application might prefer to work with:

"Vishal"

The application has changed the representation.

That’s a transformation.

Another example:

" VISHAL@EXAMPLE.COM "

could become:

"vishal@example.com"

if the application’s rules say that this normalization is appropriate.

So:

Validation
→ Is it acceptable?
Transformation
→ How should we represent it?

They’re related, but they’re not the same operation.

Why Transform Data at All?

Consistency.

Imagine different users entering the same information in slightly different ways:

Vishal
VISHAL
vishal
  Vishal
Vishal

If these representations should be treated as equivalent for a particular field, the application may normalize them.

The goal is to make downstream processing predictable.

Instead of every part of the application asking:

"Does this have spaces?"
"Is this uppercase?"
"Is this represented differently?"

you can establish a consistent representation earlier.

For example:

Incoming data
      ↓
Validate
      ↓
Normalize
      ↓
Internal representation

But Don’t Transform Everything

This is where things get subtle.

Transformation isn’t automatically good.

Suppose a user enters:

Password123

You shouldn’t casually transform it into:

password123

because you’ve changed the actual value.

Similarly, you shouldn’t blindly lowercase every piece of text just because it looks convenient.

Some values are case-sensitive.

Some values intentionally preserve their original formatting.

The correct question is:

“Does the application consider these different representations equivalent?”

If yes, normalization may make sense.

If not, preserve the original value.

Client-Side Validation Feels Like the First Line

You’ve probably seen this many times.

You type:

abc

into an email field.

Immediately the browser says:

“Please enter a valid email address.”

That’s useful.

The application doesn’t need to send an obviously incorrect request to the server just to discover the mistake.

This is client-side validation.

Its main purpose is:

Better user experience.

It gives immediate feedback.

But there’s a problem.

The browser belongs to the user.

The Server Can’t Trust the Browser

Imagine the frontend says:

Age must be greater than 18.

A user can simply bypass the frontend.

They could send a request directly to the API:

{
  "age": 12
}

If the server blindly trusts the request because:

“The frontend already checked it.”

you have a serious architectural mistake.

The server must perform its own validation.

So I think of it like this:

Client-side validation
        ↓
Helps the user
Server-side validation
        ↓
Protects the application

Both are useful.

They simply have different jobs.

TypeScript Doesn’t Solve This Problem

This was another useful distinction.

You might write:

function createUser(user: User) {
  // ...
}

and assume:

“The function requires a User, so the data must be valid."

Not at an HTTP boundary.

TypeScript helps you catch problems while developing your application.

But an external request arrives at runtime.

The TypeScript compiler doesn’t inspect every HTTP request sent to your server.

So you still need runtime validation.

The difference is:

TypeScript
→ Helps developers reason about code
Runtime validation
→ Checks actual incoming data

This distinction becomes extremely important when building APIs.

What Happens Inside a Good Request Pipeline?

A backend request might conceptually move through something like:

HTTP Request
     ↓
Parse
     ↓
Check structure
     ↓
Check types
     ↓
Check format
     ↓
Transform / normalize
     ↓
Check semantic rules
     ↓
Business logic
     ↓
Database

Not every application will implement these steps in exactly this order.

Some transformations need to happen before certain validations.

Some business rules require database access.

But the important idea is:

Don’t allow raw external input to flow unchecked through your entire application.

This Is Where Schemas Become Useful

Instead of scattering validation throughout your codebase, you can describe the expected shape of a request.

For example:

CreateUser
├── name → string
├── email → valid email
└── age → integer

A validation schema can then determine whether the incoming data satisfies that contract.

In the TypeScript ecosystem, tools such as:

  • Zod
  • Joi
  • Yup
  • Valibot
  • Ajv

can help implement these contracts.

The library isn’t the interesting part.

The architectural idea is.

You want something close to:

Untrusted Request
       ↓
Validation Schema
       ↓
Validated Data
       ↓
Application

What About Invalid Fields?

Suppose the request is:

{
  "name": "",
  "age": "hello",
  "email": "not-an-email"
}

A useful API shouldn’t simply respond:

Bad request.

It can provide structured information such as:

{
  "error": "Validation failed",
  "fields": {
    "name": "Name is required",
    "age": "Age must be a number",
    "email": "Invalid email format"
  }
}

This makes the API easier to consume.

But error messages should still avoid exposing internal implementation details.

The user needs to know how to fix the request.

They don’t need a database stack trace.

Validation Can Protect Resources Too

Validation isn’t only about correctness.

Consider pagination.

Someone requests:

limit=20

That’s reasonable.

But what if someone requests:

limit=100000000

The value might technically be a number.

But allowing it could cause your server to perform an unreasonable amount of work.

So a good API might define:

limit >= 1
limit <= 100

The same principle applies to:

  • string lengths
  • array sizes
  • uploaded file sizes
  • request body sizes
  • pagination limits
  • batch operations

A value can be valid in isolation but still be unacceptable because of its impact on the system.

Not Every Rule Belongs in Validation

Consider:

quantity must be a positive integer

This is a straightforward input constraint.

Now consider:

Premium customers can purchase 50 units.
Standard customers can purchase 10.

That’s a business rule.

It depends on who the user is and what the current product policy says.

You don’t want your input schema to become a giant container for every business rule in the application.

A cleaner mental model is:

Input validation
        ↓
"Is this request well-formed?"
Business logic
        ↓
"Is this operation allowed?"

The exact boundary varies by architecture, but keeping the concepts separate makes systems easier to reason about.

Some Rules Belong to the Database

Consider usernames.

You might first check:

"codemonkey" is available

Then create the user.

But what happens if two requests arrive at nearly the same time?

Request A → available
Request B → available
Request A → create
Request B → create

An application-level check isn’t enough to guarantee uniqueness under concurrency.

The database should enforce the invariant:

username UNIQUE

This gives us another useful model:

Client
  ↓
Helpful validation
Server
  ↓
Request validation
Business layer
  ↓
Business rules
Database
  ↓
Critical invariants

Each layer contributes something different.

The Security Angle

Validation isn’t a complete security solution.

But it is part of your application’s defensive boundary.

You should consider things like:

Unexpected fields
Oversized input
Unexpected types
Invalid formats
Invalid ranges
Malformed structures

At the same time, validation shouldn’t be used as a replacement for other security controls.

For example:

Validation does not replace parameterized database queries.

Validation does not replace safe output handling for XSS.

Validation does not replace authentication or authorization.

Different problems require different controls.

The Bigger Lesson

Before studying this topic, it was easy to think of validation as:

if (!email) {
   return error;
}

But the deeper I went, the more I realized that validation is really about trust boundaries.

Your application sits between two worlds:

External World
      │
      │  Untrusted data
      ▼
┌───────────────────┐
│ Application       │
│ Boundary          │
│                   │
│ Validate          │
│ Transform         │
│ Establish rules   │
└───────────────────┘
      │
      ▼
Internal System

The application needs a predictable representation before its internal components start making assumptions about the data.

That’s the real value of validation.

The Mental Model I’m Keeping

I find this model useful:

Incoming Data
                     ↓
             Can I parse it?
                     ↓
             Is the shape right?
                     ↓
             Are the types right?
                     ↓
             Is the format right?
                     ↓
             Does it make sense?
                     ↓
             Does it obey business rules?
                     ↓
             Does it need normalization?
                     ↓
               Use / Store

And the four concepts become:

Type
→ Is it the right kind of value?
Syntax
→ Is it correctly formatted?
Semantics
→ Does it make sense?
Transformation
→ How should we represent it?

The most important distinction is probably this:

Validation should establish whether the input satisfies the contract. Transformation should deliberately establish the representation your application wants to work with.

What I Learned From This

The interesting part of backend development isn’t always the large architectural concepts.

Sometimes it’s understanding what happens to a tiny piece of data between:

“A user typed something into a form”

and:

“The database stored it.”

There are several decisions hidden in that journey:

What did the user send?
        ↓
Can we parse it?
        ↓
Is its structure correct?
        ↓
Are its types correct?
        ↓
Does the format make sense?
        ↓
Does the value make sense?
        ↓
Should it be normalized?
        ↓
Does the operation follow business rules?
        ↓
Can it safely be stored?

That’s a much more useful mental model than simply thinking:

“Add validation to the form.”

Going Deeper

I documented the full technical version separately, where I go deeper into:

  • Runtime validation vs TypeScript types
  • Schema-based validation
  • Syntactic vs semantic validation
  • Transformation and normalization
  • Type coercion
  • DTOs and internal representations
  • Unknown fields
  • Partial updates
  • Cross-field validation
  • Business rules
  • Database constraints
  • Race conditions
  • Security boundaries
  • Request pipelines
  • Client-side vs server-side validation

If you’re learning backend development, the deeper version is where the topic starts becoming really useful from an engineering perspective.

Read the complete Developer View on my portfolio →

The browser can make suggestions.

The server makes the decision.

And the database protects the rules that absolutely cannot be broken.


메타데이터
post_id
f2113ecdd615
slug
your-application-shouldnt-trust-the-data-it-receives-f2113ecdd615
url
https://medium.com/@abhimanyug987/your-application-shouldnt-trust-the-data-it-receives-f2113ecdd615
canonical_url
https://medium.com/@abhimanyug987/your-application-shouldnt-trust-the-data-it-receives-f2113ecdd615
author_url
https://medium.com/@abhimanyug987
status
ok
fetched_at
2026-09-12 14:57:28