Validation & Transformation
Three layers of validation. Actionable error messages. Trust nothing, sanitize early.
Why Validate?
User input is untrusted by default. A missing field causes a crash. A negative price causes financial loss. An SQL injection string destroys your database. XSS payloads run code in other users' browsers.
Validation is your first line of defense. It also communicates intent — your schema is documentation of what the API expects.
Validate as early as possible in the request lifecycle — before business logic runs. Return 400 errors immediately with clear messages.
Types of Validation
Structural validation:
• Is the body valid JSON?
• Are required fields present?
• Do types match? (string where string expected)
Semantic validation:
• Is the email a real email format?
• Is the date in the future?
• Does the ID reference an existing record?
• Is the price positive?
Business validation:
• Does the user have enough balance?
• Is the discount code still valid?
• Can a user have more than 5 free accounts?
First two happen in the controller/handler layer. Third happens in the service/business logic layer.
Input Transformation
Transformation cleans and normalizes input before it reaches business logic:
- Trim whitespace from strings: " alice@test.com " → "alice@test.com"
- Lowercase emails: "ALICE@TEST.COM" → "alice@test.com"
- Parse dates: "2024-01-15" → Date object
- Convert types: "42" → 42 (string to number from query params)
- Strip dangerous characters: sanitize HTML to prevent XSS
- Default values: if limit is undefined → 20
Transform after validation, not before. Validate the raw input, then transform it into the shape you need.
Error Messages
Good validation errors are actionable and specific:
Bad: { "error": "Invalid input" }
Good: {
"error": "Validation failed",
"details": [
{ "field": "email", "message": "Must be a valid email address" },
{ "field": "age", "message": "Must be 18 or older" },
{ "field": "name", "message": "Required field is missing" }
]
}
Rules:
• Return ALL validation errors at once (not one at a time)
• Name the field that failed
• Say what the constraint is, not just that it failed
• Don't leak internal implementation details (no stack traces)
• Use consistent error response shapes across your entire API
The Backend from First Principles series is based on what I learnt from Sriniously's YouTube playlist — a thoughtful, framework-agnostic walk through backend engineering. If this material helped you, please go check the original out: youtube.com/@Sriniously. The notes here are my own restatement for revisiting later.