Skip to content

Instantly share code, notes, and snippets.

@reconbot
Last active February 13, 2024 11:19
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save reconbot/c888c0f5c4cc1ac60db14fa389259cec to your computer and use it in GitHub Desktop.
Save reconbot/c888c0f5c4cc1ac60db14fa389259cec to your computer and use it in GitHub Desktop.
A batching exchange for URQL that lets you opt out of batch queries adapted form @jakubriedl's POC This version works on non persisted queries.
// Adapted from https://gist.github.com/jakubriedl/812c2a7b26927a2249a4719555d9a0ca
import DataLoader from 'dataloader'
import { Exchange, Operation } from 'urql'
import { pipe, map } from 'wonka'
interface BatchRequest {
url: RequestInfo | string
options?: RequestInit
}
const batchFetch = (
loader: DataLoader<BatchRequest, Response>,
): typeof fetch => (url: RequestInfo, 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 = await response.json()
return bodies.map((body) => {
return {
...response,
json: () => 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), options)
return (ops$) =>
pipe(
ops$,
map((operation: Operation) => {
const fetch = shouldBatch(operation)
? batchFetch(loader)
: operation.context.fetch
return ({
...operation,
context: {
...operation.context,
fetch,
},
})
}),
forward,
)
}
@reconbot
Copy link
Author

reconbot commented Jun 8, 2021

Discussion on URQL Graphql batching urql-graphql/urql#800

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment