Skip to content

Why Validate API Requests — Complete Guide

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you'll learn Why Validate Requests. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Request validation is essential because every unvalidated input is a potential security vulnerability, data corruption risk, and source of runtime errors.

What You'll Learn

By the end of this lesson, you will identify the risks of insufficient validation, quantify the cost of validation failures, and build a security-first validation mindset.

Why It Matters

The OWASP Top 10 lists injection attacks as the number one web vulnerability. Proper validation prevents SQL Injection, XSS, Command Injection, and data corruption.

Real-World Use

Equifax's 2017 data breach (147 million records) was caused by unvalidated input in a web framework. Validation would have prevented the attack.

Security Threats Without Validation

flowchart TD
    Input[User Input] --> NoValidation[No Validation]
    NoValidation --> SQL[SQL Injection]
    NoValidation --> XSS[Cross-Site Scripting]
    NoValidation --> Command[Command Injection]
    NoValidation --> Path[Path Traversal]
    NoValidation --> Overflow[Buffer Overflow]

Injection Prevention

# injection_prevention.py
from typing import Any, Dict, List, Optional

class InjectionDetector:
    def __init__(self):
        self.patterns = {
            "sql": ["'", "\"", ";", "--", "DROP", "SELECT", "UNION", "OR 1=1"],
            "xss": ["<script>", "</script>", "onerror=", "javascript:"],
            "command": ["&&", "||", "|", ";", "`", "$("],
            "path": ["../", "..\\", "/etc/", "C:\\"],
        }

    def detect(self, value: str) -> List[str]:
        threats = []
        upper = value.upper()
        for category, patterns in self.patterns.items():
            for pattern in patterns:
                if pattern.upper() in upper:
                    threats.append(f"{category}: contains '{pattern}'")
                    break
        return threats

    def sanitize(self, value: str) -> str:
        import re
        value = re.sub(r"['\";\\]", "", value)
        value = value.replace("<script>", "").replace("</script>", "")
        value = value.replace("..", "")
        return value.strip()

detector = InjectionDetector()

inputs = [
    "John",
    "1; DROP TABLE users",
    "<script>alert('xss')</script>",
    "../../etc/passwd",
]

for inp in inputs:
    threats = detector.detect(inp)
    sanitized = detector.sanitize(inp) if threats else inp
    status = "SAFE" if not threats else f"SANITIZED ({', '.join(threats)})"
    print(f"Input: {inp:30s} -> {status:50s} -> '{sanitized}'")

Expected output:

Input: John                          -> SAFE                                            -> 'John'
Input: 1; DROP TABLE users           -> SANITIZED (sql: contains ''')                   -> '1 DROP TABLE users'
Input: <script>alert('xss')</script> -> SANITIZED (xss: contains '<script>')            -> 'alert(xss)'
Input: ../../etc/passwd              -> SANITIZED (path: contains '../')                 -> 'etc/passwd'

Data Integrity Validation

# data_integrity.py
from typing import Any, Dict, List, Optional

class IntegrityValidator:
    def validate_order(self, order: Dict) -> List[str]:
        errors = []

        # Type validation
        if not isinstance(order.get("quantity"), int):
            errors.append("quantity must be integer")
        elif order["quantity"] <= 0:
            errors.append("quantity must be positive")

        if not isinstance(order.get("price"), (int, float)):
            errors.append("price must be number")
        elif order["price"] <= 0:
            errors.append("price must be positive")

        # Range validation
        if order.get("discount", 0) < 0 or order.get("discount", 0) > 100:
            errors.append("discount must be 0-100")

        # Format validation
        if "email" in order and "@" not in order["email"]:
            errors.append("invalid email format")

        return errors

validator = IntegrityValidator()

orders = [
    {"quantity": 2, "price": 29.99, "discount": 10, "email": "a@x.com"},
    {"quantity": -1, "price": "free", "discount": 150, "email": "invalid"},
    {},
]

for order in orders:
    errors = validator.validate_order(order)
    status = "VALID" if not errors else f"INVALID: {errors}"
    print(f"Order {order}: {status}")

Expected output:

Order {'quantity': 2, 'price': 29.99, 'discount': 10, 'email': 'a@x.com'}: VALID
Order {'quantity': -1, 'price': 'free', 'discount': 150, 'email': 'invalid'}: INVALID: ['quantity must be positive', 'price must be number', 'discount must be 0-100', 'invalid email format']
Order {}: INVALID: ['quantity must be integer', 'price must be number']

Common Mistakes

1. Trusting Client-Side Validation

Client validation is for UX, not security. Server must validate everything because clients can be bypassed.

2. Validating Only Required Fields

Optional fields must also be validated. An optional email field should still be checked for format.

3. No Input Size Limits

Without length limits, an attacker can send a 1GB JSON payload and crash your server.

4. Error Messages Leaking Info

"Password incorrect for user admin" confirms the username exists. Use generic messages.

5. Not Validating All Sources

Validate body, query params, URL params, headers, and cookies. Any input source can be malicious.

Practice Questions

1. What is the most dangerous consequence of no validation?

Security vulnerabilities: SQL injection, XSS, command injection, path traversal.

2. Why is client-side validation not enough?

Clients can be modified or bypassed. Server validation is the only trusted validation.

3. What is input sanitization?

Cleaning input by stripping dangerous characters, trimming whitespace, and normalizing formats.

4. Should error messages reveal which field is invalid?

Yes, but be vague about the reason to avoid leaking information useful to attackers.

Challenge

Conduct a security audit of a sample API endpoint. List all input sources, identify validation gaps, and implement a comprehensive validation pipeline.

FAQ

Is validation only about security?

No. Validation also ensures data integrity, correct business logic execution, and good developer experience.

How much validation is enough?

Validate type, format, range, length, required fields, and business rules. When in doubt, validate.

What happens if I forget to validate?

Invalid data enters your system. It can crash the app, corrupt data, or create security vulnerabilities.

Should I use a validation library?

Yes. Libraries like Joi, Zod, or Pydantic handle edge cases and provide consistent error formats.

Can over-validation be a problem?

Rarely. Over-validation is safer than under-validation. Just ensure error messages are clear.

Mini Project: Validation Audit Tool

# validation_audit.py
from typing import Dict, List

class ValidationAudit:
    def __init__(self, endpoint: str):
        self.endpoint = endpoint
        self.inputs = ["body", "query", "params", "headers", "cookies"]
        self.validated: List[str] = []

    def mark_validated(self, source: str):
        self.validated.append(source)

    def report(self) -> Dict:
        missing = [s for s in self.inputs if s not in self.validated]
        coverage = f"{len(self.validated)}/{len(self.inputs)}"
        return {
            "endpoint": self.endpoint,
            "coverage": coverage,
            "validated": self.validated,
            "missing": missing,
            "secure": len(missing) == 0,
        }

audit = ValidationAudit("POST /users")
audit.mark_validated("body")
audit.mark_validated("query")
print(audit.report())

Expected output:

{'endpoint': 'POST /users', 'coverage': '2/5', 'validated': ['body', 'query'], 'missing': ['params', 'headers', 'cookies'], 'secure': False}

What's Next

You understand why validation matters. Next, learn schema validation, then middleware validation.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro