Release candidate — 1.0.0-rc.7
StitchAPI
GuidesTransport & adapters

The adapter seam

Swap the transport a stitch calls through — fetch, axios, xhr, or your own — without changing what the stitch promises its caller.

Set adapter when the default fetch can't carry a call the way you need it carried — to stream a response, to draw an upload bar, or to ride an HTTP client your platform already configured — and the stitch's types, validation, retry, and auth keep running on top unchanged.

The adapter is the layer a stitch hands the round trip to: take a request, return a response. A swapped transport, not a rewritten call — it changes how the bytes move, never what the stitch promises its caller.

Example

import { ,  } from 'stitchapi';

// No `adapter` field — the stitch already runs on fetchAdapter().
const  = ({
    : 'https://api.example.com',
    : '/users/{id}',
});

// One field moves the same declaration onto XMLHttpRequest, for the bytes-sent
// progress `fetch` cannot report.
const  = ({
    : 'POST',
    : 'https://api.example.com',
    : '/users',
    : (),
});

Options

adapter takes an Adapter: one function, (req) => Promise<AdapterResponse>. Leave the field off and the engine wires fetchAdapter(), so a stitch runs on fetch without ever naming it.

Three transports ship in core, and a fourth in stitchapi/testing:

  • fetchAdapter — the default. The only built-in that streams, and the one that takes an undici dispatcher for a proxy or a custom CA.
  • axiosAdapter — routes through an axios instance you supply, keeping its agent, proxy, and interceptors.
  • xhrAdapterXMLHttpRequest in the browser, for upload progress.
  • mockAdapter — canned responses, so a test drives the real engine with no network.

One rule separates a transport from the engine above it: an adapter never throws on a non-2xx status. A 500 comes back as { status: 500, ... }; only a network failure or an abort rejects. The stitch decides what a status means — whether 503 retries, whether 404 is an error or an empty result — so policy stays in the declaration and the transport stays a pipe. Writing an adapter covers the rest of the contract.

Every built-in shares one body-encoding and one response-decoding helper, so json, form, and multipart bodies go out identically and the same content-type detection reads the response back. Swapping the transport changes what the transport can do, not how a request is sent or a response is parsed.

What a transport can't do

An adapter declares the optional features it supports'stream', 'uploadProgress', 'downloadProgress' — so the engine answers for it instead of going quiet. Point a buffered-only transport at a stream or sse surface and it throws at the call. Ask one that can't report bytes sent for upload progress and an info event comes through your trace sink:

adapter.upload-progress-unsupported — onProgress is set with a request body, but
fetchAdapter cannot report upload progress — only 'direction: download' events fire.
Use xhrAdapter() to draw an upload progress bar.

Anti-pattern: don't wire onProgress to an upload bar and leave the call on the default fetch — it reports the download phase only, so the bar sits at zero until the response lands. Put the uploading stitch on adapter: xhrAdapter() or axiosAdapter(client), which read xhr.upload and axios's onUploadProgress respectively.

Declaring capabilities is opt-in: an adapter that declares nothing is treated as unknown, and no checks run against it. See Reference → Helpers for the descriptor shape and the per-adapter support matrix.

Which transport

You needReach forBecause
To startfetchAdapter() — implicitIt's the default; there's no field to write.
A stream or sse surfacefetchAdapter()The only built-in that hands back a live ReadableStream.
An upload progress barxhrAdapter()xhr.upload reports bytes sent; fetch leaves them silent.
A proxy, a custom CA, a bound interfacefetchAdapter({ dispatcher })Threads an undici Agent through one stitch.
An axios instance your platform already trustsaxiosAdapter(client)Keeps its agent, proxy, and interceptor stack intact.
Streaming in bare React NativernStreamAdapter()RN's fetch leaves response.body undefined. From @stitchapi/react-native.
Canned responses in a testmockAdapter([...])The real engine runs; only the network goes away. From stitchapi/testing.
A transport core doesn't shipa function of your ownThe contract is the extension point.

See also

On this page