Thuta Learning
BasicWeb Developmentintermediate

Response Validation

What you'll walk away with

  • Explain the core ideas behind Response Validation
  • Read the diagram and trace how a request, response, or event flows through the system
  • Explain how this applies to a real API integration you might build

Build the mental model

A status code of 200 tells you the HTTP layer worked: the server received your request and responded successfully at the protocol level. It tells you nothing about whether the JSON body inside actually contains the fields your code expects, in the types it expects.

This is the gap response validation closes, and it is easy to overlook while building against a provider's current, well-behaved API.

  • A field gets renamed
  • A number silently becomes a string
  • A value that used to always exist starts returning null
  • A new required field appears inside a nested object

If your code reads `response.data.customer.email` blindly and any link in that chain is missing or the wrong shape, you get a runtime crash, or worse, silently wrong behavior that surfaces later as corrupted data downstream.

The core discipline

Check that every field your code will use exists and has the matching type before it goes further into the application. When a check fails, fail safely and loudly instead of silently propagating malformed data.

text
RESPONSE VALIDATION DECISION FLOW
---------------------------------
Response arrives (status 200)
        |
        v
Check required fields exist
Check each field's type matches
        |
        v
     Valid? ------ NO ---> Fail safely: clear error,
        |                  do not use the data
       YES
        |
        v
  Use the validated data
  in the rest of your app

Connect it to a real scenario

`validateResponse` takes a parsed response object and a small shape description mapping each required field name to its expected JavaScript type. It walks the shape, checking each field is present, is not null, and matches the type, collecting every problem rather than stopping at the first.

The valid example is a charge response with a nested customer object matching the shape exactly, so the function returns `{ valid: true, data: ... }`, safe to use as-is.

The malformed example simulates a real provider regression: `amount` arrived as a string, `status` arrived as null, and `customer` is missing entirely. The function does not throw or crash; it returns `{ valid: false, errors: [...] }` listing all three problems in one pass.

Try the working example

javascript
const expectedShape = {
  id: "string",
  amount: "number",
  status: "string",
  customer: "object"
};

function validateResponse(response, shape) {
  const errors = [];
  for (const [field, type] of Object.entries(shape)) {
    if (!(field in response)) {
      errors.push(`missing field: ${field}`);
      continue;
    }
    const value = response[field];
    if (value === null) {
      errors.push(`field "${field}" is null, expected ${type}`);
      continue;
    }
    const actualType = Array.isArray(value) ? "array" : typeof value;
    if (actualType !== type) {
      errors.push(`field "${field}" is ${actualType}, expected ${type}`);
    }
  }
  return errors.length === 0 ? { valid: true, data: response } : { valid: false, errors };
}

const validResponse = { id: "ch_123", amount: 5000, status: "succeeded", customer: { id: "cus_1" } };
const malformedResponse = { id: "ch_124", amount: "5000", status: null };

console.log(validateResponse(validResponse, expectedShape));
console.log(validateResponse(malformedResponse, expectedShape));
You should see
validResponse -> { valid: true, data: { id: 'ch_123', amount: 5000, status: 'succeeded', customer: { id: 'cus_1' } } }
malformedResponse -> { valid: false, errors: [
  'field "amount" is string, expected number',
  'field "status" is null, expected string',
  'missing field: customer'
] }

5-minute try-it

Add a new field, `currency: "string"`, to `expectedShape`. Run it against `validResponse` without adding a `currency` field to that object, and see how the error output changes.

One important caution

Accessing a deeply nested field like `response.data.customer.email` without checking each link in the chain

Skipping validation entirely because you assume the provider's API will never change

typeof - MDNAPI Integration & Webhooks

Easy traps

  • Accessing a deeply nested field like `response.data.customer.email` without checking each link in the chain
  • Skipping validation entirely because you assume the provider's API will never change
  • If you haven't taken the API Tutorial yet, it's worth finishing that first -- this course doesn't re-teach REST/HTTP/auth basics, it builds on top of them with webhooks, testing, reliability, and integration architecture.

Exercise

Add a new field, `currency: "string"`, to `expectedShape`. Run it against `validResponse` without adding a `currency` field to that object, and see how the error output changes.

You'll know it worked when: validResponse -> { valid: true, data: { id: 'ch_123', amount: 5000, status: 'succeeded', customer: { id: 'cus_1' } } } malformedResponse -> { valid: false, errors: [ 'field "amount" is string, expected number', 'field "status" is null, expected string', 'missing field: customer' ] }

Response Validation | Thuta Learning