Input Processing and Validation
By Syncfusion®, Inc. · Last updated
Reading JSON, form data and headers, converting values, the Post-Redirect-Get pattern, validation rules, validation middleware, 400 versus 422, sanitization with trimming and allowlists, and validation libraries like Zod.
What you will learn
- Read JSON, form data and headers safely
- Convert strings to numbers, booleans and defaults
- Use the Post-Redirect-Get pattern
- Write reusable validation middleware
- Sanitize input with trimming, normalising and allowlists
Input Processing Introduction
Input = any data a client sends: params, query, body and headers.
- Input can be missing, wrong, too big or harmful.
- Validation = checking that input follows your rules. Bad input is rejected.
- Sanitization = cleaning input, such as trimming spaces. It changes the value.
- Validate on the server, even if the web page already checks.
- Anyone can skip your web page and send requests with curl.
Simple lesson: Never trust input. Check it, clean it, then use it.
A Real-World Example
The canteen accepts written order slips.
- A slip says "Dosa x 500". Nobody orders 500 dosas.
- Another slip has no name on it.
- Another has the name written with extra spaces and capital letters.
- The counter staff check each slip before the kitchen sees it.
Rejecting the first two is validation. Tidying the name is sanitization.
In this lesson, you will learn:
- How to read JSON, form data and headers.
- How to convert strings into numbers, booleans and defaults.
- The Post-Redirect-Get pattern for forms.
- How to write validation rules and validation middleware.
- When to answer 400 and when to answer 422.
- How to sanitize input, and the limits of sanitization.
Reading JSON Input
req.bodyholds the parsed JSON. See Body Parsers and Third-Party Middleware.- Nested objects:
req.body.student.name. - Arrays:
req.body.itemsmay be a list. - Missing values: a field may be
undefined. Use?.and??. - Unexpected values: a number may arrive as a string, or a list as a single value.
- Malformed JSON: the parser rejects it with 400 before your route runs.
?.stops a crash whenstudentis missing.[].concat(...)turns one item, many items or nothing into a list.
Simple lesson: Expect missing and odd values. Read them carefully.
Reading Form Data
An HTML form sends each field as name=value. Different fields behave differently.
- Text input: a string. Empty boxes send
"". - Checkbox: sends its value only when ticked. Unticked boxes send nothing.
- Several checkboxes with one name: an array when two or more are ticked.
- Radio buttons: one value, the chosen one.
- Select: one value. A
multipleselect works like checkboxes. - action = the URL the form posts to. method =
postfor changes.
<form action="/feedback" method="post">
<input name="name">
<input type="checkbox" name="likes" value="idli"> Idli
<input type="checkbox" name="likes" value="vada"> Vada
<input type="radio" name="rating" value="5"> 5 stars
<select name="meal"><option>Breakfast</option><option>Lunch</option></select>
<input type="checkbox" name="subscribe" value="yes"> Send me the menu
<button>Send</button>
</form>
- In the second request,
likesis one string, not a list.[].concatfixes it. subscribeis missing, so it becomesfalse.
Simple lesson: Forms send only strings, and unticked boxes send nothing.
Reading Header Input
- Content-Type: what format the body is in.
req.is("json")checks it. - Accept: what format the client wants back.
req.accepts("json")checks it. - Authorization: login details, like
Bearer abc123. - Custom headers: names your app chooses, like
X-Student-Id. - Read any header with
req.get(name). Missing headers areundefined.
Simple lesson: Headers are input too. Split, check and look up before trusting.
Converting Values
Most input arrives as text. Convert it on purpose.
- Strings:
String(value ?? "").trim(). - Numbers:
Number(value), then checkNumber.isFiniteorNumber.isInteger. - Booleans: compare text:
value === "true"orvalue === "on". - Defaults: use
??so0and""are not replaced by mistake. - Empty values: decide if
""means "not given" or "blank on purpose".
Easy way to remember: ?? keeps 0 and "". || replaces them. Use ?? for defaults.
Simple lesson: Convert first, then check the converted value.
The Post-Redirect-Get Pattern
After a form post, refreshing the page can send the form again. Two orders!
- POST: the browser sends the form.
- Redirect: the server saves it and answers 303 See Other with a new URL.
- GET: the browser loads that URL. Refresh now only repeats the GET.
- If the form has errors, do not redirect. Show the form again with the user's values and the error messages.
- A good order gets 303 with a
Locationheader. A browser follows it. - A bad order gets the form again, with the quantity still filled in.
Simple lesson: Redirect after a successful POST. Re-show the form after a failed one.
Validation Rules
Common rules for each field:
- Required: the value must be present and not empty.
- String: a type check plus a length range, like 2 to 50 characters.
- Number: a real number in a range, like a quantity from 1 to 10.
- Boolean: exactly true or false.
- Date: a real date, maybe not in the past.
- Enum: one of a fixed list, like
["breakfast", "lunch"]. - Array and nested object: check the shape, then each item.
- Cross-field: two fields together, like "pickup time must be after order time".
Validate every place input comes from: params, query, body and headers.
Simple lesson: Write each rule down. If it is not checked, it is not true.
Validation Middleware
Keep validation out of your route handlers.
- Write a reusable function that checks the body against rules.
- Wrap it in middleware that runs before the route.
- Valid: call
next(). The route can trustreq.body. - Invalid: answer with a list of errors.
- 400 Bad Request: the request is broken, such as malformed JSON or the wrong type.
- 422 Unprocessable Content: the format is fine, but the values break your rules.
- The second request gets 422 with all three problems at once.
- The third is an array, not an object, so it gets 400.
Easy way to remember: 400 = "I can't read this". 422 = "I read it, and it's wrong".
Simple lesson: Validate in middleware, list every error, and pick 400 or 422 correctly.
Sanitization
Sanitization cleans input before you store or use it.
- Trim: remove spaces at the start and end.
- Normalise: make one form, such as lowercase emails.
- Allowlist fields: copy only the fields you expect.
- Remove unexpected fields: never pass
req.bodystraight to the database. - Context matters: escape for HTML in templates, use query parameters for SQL.
isAdminis dropped. A student cannot make themselves an admin.nameandemailare tidy.
Sanitizing is not enough on its own. Still validate after cleaning, and still escape output. A trimmed value can still be wrong or harmful.
Simple lesson: Clean what you keep, and keep only what you expect.
Validation Libraries
Hand-written rules get long. Libraries describe rules as a schema.
- Schema = one description of the shape and rules of your data.
- Zod: schemas in plain JavaScript. Also gives you clean, converted data.
- express-validator: a chain of checks written as middleware.
- Joi: an older, mature schema library.
- Pick one per project. Zod is a popular choice for new projects.
import { z } from "zod";
const Order = z.object({
item: z.string().trim().min(1),
qty: z.coerce.number().int().min(1).max(10),
meal: z.enum(["breakfast", "lunch"]),
});
app.post("/orders", (req, res) => {
const result = Order.safeParse(req.body);
if (!result.success) {
return res.status(422).json({ errors: result.error.issues });
}
res.status(201).json(result.data); // trimmed and converted
});
Simple lesson: A schema library replaces many if checks with one clear description.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| Only checking in the browser | The page already validated | Anyone can skip the page | Bad data reaches the server |
if (req.body.qty) | It checks the number | 0 and "0" behave oddly | Wrong orders pass or fail |
Saving req.body directly | The client sends only expected fields | Clients can add any field | Users set isAdmin: true |
| Showing the first error only | One message is enough | List every error | Users fix one, then hit the next |
| 200 for invalid input | The body explains it | Use 400 or 422 | Clients think it worked |
| No redirect after a form POST | Rendering is fine | Use Post-Redirect-Get | Refresh creates duplicate orders |
Check Yourself
Exercises
What to Remember
- All input is untrusted: params, query, body and headers.
- Forms send strings, and unticked checkboxes send nothing.
- Convert values on purpose, then check them.
- Validate in middleware. Answer 400 for unreadable input, 422 for broken rules.
- Sanitize with trim, normalise and allowlist, and still validate after.
- Use Post-Redirect-Get after successful form posts.
Next: Cookies and Sessions. You will remember who a user is between requests.