Response Validation

The Course
•
5 min read

Keep malformed success responses out

A response with status 200 can still be unusable. Validate its parsed body before giving it to the rest of the app. A decoder should either return normalized records or fail clearly; callers should not have to guess whether half the fields are usable.

The small decoder below expects an object with a nonnegative integer count. It checks null, object shape, arrays, and the field. We will apply the same boundary to an array of book records in the final project. Unknown extra properties may be ignored if the contract says so; missing required fields may not.

Store the result only after every required check succeeds. Assigning parsed raw data to state and then checking it can briefly render bad data, or leave corruption behind after failure. Prepare a valid next value first, then commit the change.

Catch boundaries need to distinguish programming defects from expected user-facing failure. You can show a message at a UI boundary and still keep diagnostics available during development. Do not silently substitute zero for a malformed count: that changes “unknown” into a plausible but false business value.

Predict and trace

Write the output before running. Follow the values through each call; if your prediction differs, identify the first step that surprised you.

Predict, then run JavaScript
function decodeCount(body) {
  if (
    body === null ||
    typeof body !== "object" ||
    Array.isArray(body) ||
    !Number.isInteger(body.count) ||
    body.count < 0
  ) {
    throw new Error("Invalid count");
  }

  return body.count;
}

let current = 7;

try {
  current = decodeCount({ count: "3" });
} catch (error) {
  console.log(error.message);
}

console.log(current);
console.log(decodeCount({ count: 0 }));
Check your prediction
Invalid count
7
0

The assignment does not happen because the right-hand function throws. Zero is a valid count, not an absent value.

A response can succeed while its body is unusable

Separate transport status from domain decoding. A server can return HTTP success with null, text counts or missing fields. A good decoder treats these as data failures and leaves the caller’s last valid state intact.

Conversion is not validation here. If the API contract says count is a number, accepting “3” by coercion may hide a server regression. Normalize fields you deliberately permit to normalize; reject fields whose representation is part of the contract.

A response can succeed while its body is unusable: trace JavaScript
function valid(body) {
  return (
    body !== null &&
    typeof body === "object" &&
    !Array.isArray(body) &&
    Number.isInteger(body.count) &&
    body.count >= 0
  );
}

console.log(valid({ count: 0 }));
console.log(valid({ count: "0" }));
console.log(valid(null));
Check your prediction
true
false
false

The guard checks the container before reading a field. Short-circuiting prevents access on null. Zero passes the domain rule while text zero does not.

A response can succeed while its body is unusable: contrast JavaScript
let current = 7;
const candidate = { count: "3" };

if (Number.isInteger(candidate.count) && candidate.count >= 0) {
  current = candidate.count;
}

console.log(current);
console.log(typeof current);
Check your prediction
7
number

The rejected candidate never becomes state. A broad assignment before decoding would force recovery code to repair data that should never have been accepted.

Try the rule on new inputs

Test missing count, negative count, a fraction and an array with a count property. Keep the current value unchanged for every rejection, then prove that a valid zero replaces it.

Build it yourself

Exercise

Response Validation: practise

Write decodeTitle(body), requiring a nonblank string title in a non-array object. Return the trimmed title. Print Notes and a caught error for { title: null }.

Your working space JavaScript
// Do not coerce null into text.
Hint 1: the idea

Validate the container, then the field type, then trimmed contents.

Hint 2: a first step

Keep short-circuit guards before accessing string methods.

Show one solution
function decodeTitle(body) {
  if (
    body === null ||
    typeof body !== "object" ||
    Array.isArray(body) ||
    typeof body.title !== "string" ||
    body.title.trim() === ""
  ) {
    throw new Error("Invalid title");
  }

  return body.title.trim();
}

console.log(decodeTitle({ title: " Notes " }));

try {
  decodeTitle({ title: null });
} catch (error) {
  console.log(error.message);
}
Notes
Invalid title

Try null, [], {}, and title: ” ”. Then permit an optional author field and state whether wrong-type optional data is rejected or ignored.

Test a changed requirement

Try null, [], {}, and title: ” ”. Then permit an optional author field and state whether wrong-type optional data is rejected or ignored.

Explain before moving on

Where should the decoder sit relative to rendering? Why is returning an empty default different from rejecting malformed data?

Was this article helpful?

Your response is saved on this device.