Menu

Topic 1 of 8

Request Response Validation

Learn Request Response Validation for free with explanations, exercises, and a quick test (for Backend Engineer).

Published: January 20, 2026 | Updated: January 20, 2026

Who this is for

  • Backend Engineers building or maintaining HTTP/JSON APIs.
  • Developers integrating services who need robust contracts between systems.
  • Anyone aiming to reduce bugs by catching bad inputs/outputs at the boundary.

Prerequisites

  • Comfort with HTTP basics: methods, status codes, headers, and JSON.
  • Ability to read small code snippets (Node.js/TypeScript, Python, or Java) and JSON Schema.
  • Basic understanding of API routing and middleware.

Learning path

  1. Understand why validation matters and the boundary-first mindset.
  2. Learn core rules: request and response schemas, error formats, and status codes.
  3. Practice with worked examples (POST/GET/PATCH scenarios).
  4. Do the exercises to design schemas and error responses.
  5. Take the quick test to check understanding. Note: test is available to everyone; only logged-in users get saved progress.
  6. Build a small project with runtime validation.

Why this matters

  • Prevents invalid data from entering your system (costly bugs, security issues).
  • Keeps your API contract stable so clients don’t break unexpectedly.
  • Makes debugging faster with consistent error formats and messages.
  • Improves DX: auto-generated docs, typed clients, and predictable responses.

Concept explained simply

Request/response validation checks that incoming data from a client and outgoing data from your service strictly match an agreed contract (schema). Validate at the edges: before business logic runs and before the response leaves.

Mental model

Imagine your API as a nightclub with two bouncers:

  • Request bouncer: checks IDs (types), dress code (formats), and capacity (sizes) before letting anyone in.
  • Response bouncer: ensures every guest leaving wears a wristband (required fields) and no one sneaks out with something they shouldn’t (forbidden fields).

Key concepts and rules

  • Validate everything at the boundary: path params, query params, headers, body, and files.
  • Be explicit about types and formats: integer vs number, min/max, string patterns, enums, date-time, email, uuid.
  • Use strict mode by default: reject unknown fields or explicitly strip them.
  • Respond with consistent errors: status code, stable error code, message, and field-level details.
  • Validate responses too: keep contracts stable and avoid leaking internal fields.
  • Backward compatibility: add optional fields; avoid renaming/removing fields in existing versions.
  • Performance: compile and reuse schemas; validate at the edges only once.
Tip: Typical tools
  • JSON Schema + AJV (Node.js), class-validator (Java/Spring), Pydantic (Python/FastAPI), Zod/Joi (Node.js).
  • Schema-first with OpenAPI or code-first that emits OpenAPI for docs and clients.

Worked examples

Example 1 — Node.js (Express + Zod): POST /users

Goal: Validate request body and the response you send back.

// Schema
const UserCreate = z.object({
  name: z.string().min(2).max(50),
  email: z.string().email(),
  age: z.number().int().min(13).max(120).optional(),
  roles: z.array(z.enum(["user","admin"]))
    .min(1),
});
const UserPublic = z.object({
  id: z.string().uuid(),
  name: z.string(),
  email: z.string().email(),
  roles: z.array(z.enum(["user","admin"]))
});

// Middleware-like validation
app.post('/users', (req, res) => {
  const parsed = UserCreate.safeParse(req.body);
  if (!parsed.success) {
    return res.status(400).json({
      error: {
        code: "VALIDATION_ERROR",
        message: "Invalid request body",
        details: parsed.error.issues
      }
    });
  }
  const user = createUser(parsed.data);
  const out = UserPublic.parse(user); // response validation
  return res.status(201).json(out);
});
Example 2 — Python (FastAPI + Pydantic): GET /search
class SearchQuery(BaseModel):
    q: constr(min_length=1)
    limit: conint(ge=1, le=100) = 10
    page: conint(ge=1) = 1

class Item(BaseModel):
    id: UUID
    title: str

class SearchResponse(BaseModel):
    total: conint(ge=0)
    items: List[Item]

@app.get("/search", response_model=SearchResponse)
async def search(q: str, limit: int = 10, page: int = 1):
    # FastAPI validates query params automatically by type hints/validators
    results = query_index(q, limit, page)
    return SearchResponse(**results)
Example 3 — JSON Schema (AJV): PATCH /orders/{id}

Validate a path param (uuid) and partial body.

// Path param (often validated by router or custom middleware)
const IdSchema = { type: "string", format: "uuid" };

// Body schema: partial update
const OrderPatchSchema = {
  type: "object",
  additionalProperties: false,
  properties: {
    status: { enum: ["placed","paid","shipped","cancelled"] },
    notes: { type: "string", maxLength: 500 },
  },
  minProperties: 1
};

// Error shape example
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request",
    "details": [
      { "path": ["status"], "message": "must be equal to one of the allowed values" }
    ]
  }
}

Hands-on: Follow these steps

  1. Define the contract: Write request and response schemas for your endpoint. Be explicit about required/optional and unknown fields.
  2. Add request validation: Place a middleware/guard that checks headers (Content-Type), params, query, then body. Fail fast with a consistent error.
  3. Add response validation: Validate or serialize the object you send out. Strip internal fields, enforce types.
  4. Error format: Return { error: { code, message, details } } with appropriate 4xx/5xx.
  5. Test: Try valid and invalid payloads (missing required, wrong type, extra fields). Confirm statuses and messages.
  6. Performance: Compile/reuse schemas; do not re-validate inside business logic.

Exercises

Do these in your editor or mentally. Then open the solutions below each exercise card on this page.

  • Exercise 1: Design a JSON Schema for a POST /users body and a 400 error shape.
  • Exercise 2: Design a 200/404 response contract for GET /orders/{id}, including types and constraints.
Self-check checklist
  • Did you limit unknown fields on requests?
  • Are all numeric ranges and string lengths explicit?
  • Do you provide a stable error code and field-level details?
  • Do 200 responses match the documented shape exactly?

Common mistakes and self-check

  • Trusting the client: Always validate; do not rely on UI validation.
  • Silent coercion: Avoid auto-casting strings to numbers; parse explicitly or reject.
  • Inconsistent error shapes: Standardize and reuse one structure.
  • Status code mismatch: 200 with error body is confusing; use 4xx/5xx properly.
  • Forgetting response validation: You might leak internal fields or return nulls where arrays are expected.
  • Over-validation: Rejecting new optional fields during rollout; decide policy (reject or strip) and document it.
  • Performance pitfalls: Re-creating validators per request; compile once and reuse.
How to self-test quickly
  • Send bad inputs via curl or an API client: wrong types, extra fields, large payloads.
  • Randomize values (fuzz) for a minute; the service should fail gracefully with 400, not 500.
  • Contract-test your responses: verify types and required fields in automated tests.

Practical projects

  • Build a User service with POST /users and GET /users/{id}, including full request/response validation and a consistent error format.
  • Add a Search endpoint with validated query params (pagination, limits) and typed responses.
  • Introduce versioning: v1 to v1.1 adds optional fields without breaking existing clients; prove with tests.

Quick Test

This quick test is available to everyone. Only logged-in users will have their progress saved.

When you're ready, open the test below.

Next steps

  • Apply validation to at least two more endpoints in your project.
  • Add automated contract tests to CI to prevent regressions.
  • Explore emitting or consuming OpenAPI to keep docs and code in sync.

Mini challenge

Extend an existing endpoint to accept an optional field (e.g., user.timezone). Update request/response schemas, set sensible defaults server-side, and prove backward compatibility by running previous tests unchanged.

Practice Exercises

2 exercises to complete

Instructions

Create a JSON Schema for POST /users with these rules:

  • name: string, 2–50 chars
  • email: valid email
  • age: integer 13–120 (optional)
  • roles: array of enum ["user","admin"], min 1
  • password: string, min 12 chars
  • Reject unknown fields

Also draft a 400 error JSON shape with a stable code, message, and field-level details array.

Expected Output
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request body", "details": [ {"path": ["password"], "message": "must NOT have fewer than 12 characters"} ] } }

Request Response Validation — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Request Response Validation?

AI Assistant

Ask questions about this tool