Skip to content

Exchanges and result extraction

A result extractor controls the runtime result, independently of the TypeScript generic on request<R> or get<R>. Choose it in RequestOptions.resultExtractor.

Extractor selection

ResultExtractor<R> is (exchange: FetchExchange) => R | Promise<R>; ResultExtractorCapable declares its optional field. ResultExtractors is a lookup object for these exact exports:

Property / exported functionResolved valueBody use
Exchange / ExchangeResultExtractorThe same FetchExchangeNone
Response / ResponseResultExtractorexchange.requiredResponseNone
Json / JsonResultExtractorParsed JSON (any)Response.json()
Text / TextResultExtractorstringResponse.text()
Blob / BlobResultExtractorBlobResponse.blob()
ArrayBuffer / ArrayBufferResultExtractorArrayBufferResponse.arrayBuffer()
Bytes / BytesResultExtractorUint8Array<ArrayBuffer>Response.bytes(); runtime must support it.

JSON types are compile-time promises, not runtime validation. The package augments Response.json<T = any>(): Promise<T> for typing only. An empty 204 response still fails JSON parsing; choose Response or a custom extractor for an empty body.

FetchExchange

new FetchExchange(init: FetchExchangeInit) requires fetcher and request; it accepts optional resultExtractor, response, error, and attributes. AttributesCapable.attributes accepts a record or Map; the constructor copies entries into a new Map. The extractor defaults to Exchange. The request and response are references, not clones.

MemberContract
ensureRequestHeaders()Returns existing headers or assigns/returns {}.
ensureRequestUrlParams()Ensures both .path and .query records and returns Required<UrlParams>.
hasError(), hasResponse()Boolean truthiness checks.
requiredResponseReturns response; throws ExchangeError if absent.
extractResult<R>(): Promise<R>Computes once and caches the value or promise; concurrent calls reuse it.
response = valueReplaces response and invalidates the cached result.

An asynchronous extraction rejection remains cached. A synchronous throw before a value/promise is returned leaves the cache unpopulated, so another call can retry. Changing only resultExtractor does not reset an already-populated cache. Body-reading operations consume the response once; the cache avoids repeated parsing through extractResult, not direct repeated response.json() calls.

Extraction occurs after the interceptor manager finishes; an extractor's parse error is not sent back through error interceptors. Catch the rejected request/iteration at the caller. Streams returned by custom extractors remain caller-owned; cancel or finish them.

Public utility types

PartialBy<T, K> makes selected keys optional; RequiredBy<T, K> makes selected keys required; RemoveReadonlyFields<T> removes readonly keys while preserving writable ones. These have no runtime behavior or data validation.

Complete example

ts
import { Fetcher, FetchExchange, ResultExtractors } from '@ahoo-wang/fetcher';

type User = { id: string; name: string };
const client = new Fetcher();
const exchange = new FetchExchange({
  fetcher: client,
  request: { url: '/users/1' },
  response: Response.json({ id: '1', name: 'Ada' }),
  resultExtractor: ResultExtractors.Json,
});
const first = await exchange.extractResult<User>();
const second = await exchange.extractResult<User>();
console.assert(first === second && first.name === 'Ada');

Public symbols and source

SymbolImplementation
AttributesCapablefetchExchange.ts:23
FetchExchangeInitfetchExchange.ts:41
FetchExchangefetchExchange.ts:105
ResultExtractorresultExtractor.ts:23
ResultExtractorCapableresultExtractor.ts:31
ExchangeResultExtractorresultExtractor.ts:42
ResponseResultExtractorresultExtractor.ts:55
JsonResultExtractorresultExtractor.ts:67
TextResultExtractorresultExtractor.ts:79
BlobResultExtractorresultExtractor.ts:92
ArrayBufferResultExtractorresultExtractor.ts:106
BytesResultExtractorresultExtractor.ts:120
ResultExtractorsresultExtractor.ts:131
PartialBytypes.ts:33
RequiredBytypes.ts:52
RemoveReadonlyFieldstypes.ts:85

Package index

Released under the Apache License 2.0.