@wener/console
Version:
Base console UI toolkit
80 lines (72 loc) • 2.29 kB
text/typescript
// https://gist.github.com/reconbot/c888c0f5c4cc1ac60db14fa389259cec
import DataLoader from 'dataloader';
import { LRUCache } from 'lru-cache';
import type { Exchange, Operation } from 'urql';
import { map, pipe } from 'wonka';
interface BatchRequest {
url: RequestInfo | URL;
options?: RequestInit;
}
const batchFetch =
(loader: DataLoader<BatchRequest, Response>): typeof fetch =>
(url: RequestInfo | URL, options?: RequestInit) => {
return loader.load({ url, options });
};
const loadBatch = (fetcher: typeof fetch) => async (requests: Readonly<BatchRequest[]>) => {
// if batch has just one item don't batch it
if (requests.length === 1) return [await fetcher(requests[0].url, requests[0].options)];
const requestBody = requests
.map((req) => JSON.parse(req.options?.body?.toString() ?? '{}'))
.map((body) => ({
query: body.query,
operationName: body.operationName,
variables: body.variables,
extensions: body.extensions,
}));
const response = await fetcher(requests[0].url, {
...requests[0].options,
body: JSON.stringify(requestBody),
});
const bodies: object[] = await response.json();
const { status, statusText, ok, headers, url } = response;
return bodies.map((body) => {
return {
url,
headers,
status,
statusText,
ok,
json: async () => body,
text: async () => JSON.stringify(body),
} as Response;
});
};
// You want to put your own logic here - I want to opt out of batching per `useQuery({ query, context: useMemo(() => ({ batch: false }), []) })`
// but you do you!
const shouldBatch = (operation: Operation): boolean => {
return operation.kind === 'query' && (operation.context.batch ?? true);
};
export const batchFetchExchange =
(options?: DataLoader.Options<BatchRequest, Response>, fetcher = fetch): Exchange =>
({ forward }) => {
const loader = new DataLoader(loadBatch(fetcher), {
// short-lived cache
cacheMap: new LRUCache<any, any>({ max: 2000, ttl: 60 * 1000 }),
...options,
});
return (ops$) =>
pipe(
ops$,
map((operation: Operation) => {
const fetch = shouldBatch(operation) ? batchFetch(loader) : operation.context.fetch;
return {
...operation,
context: {
...operation.context,
fetch,
},
};
}),
forward,
);
};