Stop 16 · Security Across the StackTopics · 2/7

Input Validation

Part of the Full Stack Developer Roadmap

In short

Every field in a request body is a stranger's opinion. Validate it at the door, and the rest of your code gets to work with clean, predictable data.

Validating user input at every layer it passes through — client-side for UX, server-side as the actual security boundary — since a full-stack app has multiple places data enters the system.

The blob at a railway luggage scanner pulls out a bag holding a spiky object.

Think of it like this

The station luggage scanner

At a big railway station, every bag rolls through the scanner before it reaches the platform. Most are fine. The odd one with something dangerous is pulled out right there, not on the train. Validation is that scanner for your API.

Watch it happen

A signup request through the checks

Raw body
Parse JSON
Validate
Normalise
Handler

1/5The body arrives: { "email": " Riya@Mail.com ", "age": "19", "role": "admin" }.

The big ideas

What you need to know

Validate on the server

Frontend checks are for user comfort. Attackers skip your form and send raw requests, so the server must check again.

Allowlist, not blocklist

Describe what good input looks like: type, length, format. Reject everything else instead of chasing bad patterns.

Sanitise means clean up

Trim spaces, normalise case, strip unknown fields and unsafe HTML. It makes data consistent, not magically safe.

One schema, many uses

A Zod schema gives runtime checks and TypeScript types together, so your types can't drift from your checks.

See it in code

Walk through the code

ts
1import { z } from "zod";2 3const SignupSchema = z.object({4  email: z.email(),5  age: z.number().int().min(16).max(100),6  name: z.string().trim().min(1).max(50),7});8 9app.post("/signup", (req, res) => {10  const result = SignupSchema.safeParse(req.body);11  if (!result.success) return res.status(400).json(result.error.issues);12  createUser(result.data); // only clean, typed data gets here13  res.status(201).end();14});

1/3The schema describes good input: a real email, a whole-number age in range, and a trimmed name of sane length.

z.object strips keys you didn't declare, so a sneaky "role": "admin" never reaches createUser. That alone blocks a whole class of mass-assignment bugs.

Save yourself the pain

Common mistakes

Oops

Passing req.body straight into db.user.create().

Do this instead

Pick fields through a schema first. Otherwise a user can set isAdmin or balance just by adding it to the JSON.

Oops

Trusting that the React form already validated it.

Do this instead

Run the same rules on the server. Share the Zod schema between both if you like.

Your mission

Put a scanner on one endpoint

~30 min

Quick check

Why is z.object useful against mass-assignment attacks?

Go deeper

  1. 1Add validation to a form on the frontend and re-validate the same data on the backend.
  2. 2Confirm the backend rejects invalid data even if you bypass the frontend entirely, e.g. via Postman.
  3. 3Never trust that 'the frontend already checked this.'

More topics at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge