Release candidate — 1.0.0-rc.3
StitchAPI

Inspect what the server actually sent

Probe a fresh call with .inspect() to read the unredacted raw body next to the validated value and the drift findings diffed between them — without throwing — when you need to see what changed after the fact.

Task

The schema coerced a field, filled a default, or stripped a key — and now you want to see what the server actually sent. An await getUser(...) hands back only the validated value; the raw body and the soft drift findings rode the event stream and are gone. You want both, on the resolved result, without consuming a stream and without throwing when the response breaks the contract.

Example

.inspect() runs one fresh call and resolves to an Inspection{ value, raw, findings, status, error }. It never throws: a hard contract violation comes back as value: null with error set, and raw / findings / status still populated.

import { ,  } from 'stitchapi';
import {  } from 'zod';

const  = ({
    : 'https://demo.stitchapi.dev',
    : '/users/{id}',
    : 'data',
    : (
        .({ : .(), : .(), : .() }),
    ),
});
const  = await .({ : { : 1 } });

.; // the validated User (coerced/defaulted/stripped) — or null on a hard fail
.; // every drift finding: undeclared, coerced, defaulted, invalid
.; // the HTTP status, so `raw` is interpretable (a 422 body reads unlike a 200)
.; // a StitchError on a contract violation, else null — value/error are inverse

// `raw` is the pre-validation body the findings are diffed against. Reach for it
// deliberately (see the caveat below); here, only when something actually drifted.
if (.. > 0) {
    .('server sent:', .);
}

value is the same validated value await getUser(...) returns; raw is the body before validation, the coordinate space each finding.path is anchored to. The pair is self-explaining: a coerced finding at role plus result.raw.role tells you exactly which wire value the schema rewrote.

How it works

.inspect() consumes one run with the raw body retained and surfaces it on a non-enumerable field. The other four fields are ordinary properties; raw alone is hidden, so it cannot leak into a log by accident.

Anti-pattern: don't log the whole wrapper — raw is unredacted, so a stray token or PII in an undeclared field would land in your logs. Because raw is non-enumerable, JSON.stringify(result), {...result}, and trace sinks all skip it; that protection only holds if you don't defeat it. Read result.raw deliberately, at the one place you mean to.

.inspect() always hits the network. It is a fresh probe — it bypasses the cache by default, reading and writing neither — so it is not an observer of what your cached await call did. Pass { cache: true } to honour the cache policy instead; on a cache hit the entry stores only { value, status }, so raw is then null.

import {  } from 'stitchapi';

const  = ({
    : 'https://demo.stitchapi.dev',
    : '/users/{id}',
    : { : '60s' },
});
// Honour the cache; on a hit, `raw` is null (the cache holds no raw body).
const  = await .({ : { : 1 } }, { : true });

raw is null on a streaming surface too — the engine refuses to buffer an unbounded delta spine — while findings and status still populate; use .stream() for incremental inspection there.

See also

On this page