How to skip cache for some operations #3559
-
ContextOn some page we use notifications implemented as URQL subscription. The user can receive many notifications (up to 50RPS). This data is not persistent (cursors, peer states, etc...) Every time I receive message from subscription it goes throw cache process: writes result to cache, and then GC deletes it because nothing have reference to this data. Now I am looking for a way to not use cache-exchange at all for some operations. My tryI tried to wrap cache-exchange with my own Exchange like this: interface WrappedCacheExchangeOpts extends CacheExchangeOpts {
cacheSkipOperations?: string[];
}
/** filter which operation will skip cache */
function filterOperationToSkipCache(
operation: Operation,
options?: WrappedCacheExchangeOpts
) {
const operationName = getDocumentFieldName(operation.query);
if (operationName && options?.cacheSkipOperations?.includes(operationName)) {
return true;
}
return false;
}
/**
* Wraps urql cache exchange, with ability to skip cache (request and response)
*
* To skip cache add operation name to options.cacheSkipOperations array
*/
function buildCacheExchangeWrapper(
options: WrappedCacheExchangeOpts
): Exchange {
const originalCacheExchange = cacheExchange(options);
return (input) => {
return (operations) => {
// just forward without changes
const forwardedOps = pipe(
operations,
filter((operation) => filterOperationToSkipCache(operation, options)),
input.forward
);
// use cache pipe
const cachedOps = pipe(
operations,
filter((operation) => !filterOperationToSkipCache(operation, options)),
originalCacheExchange(input)
);
return merge([cachedOps, forwardedOps]);
};
};
} But when i start application, i get error: Is there any way to achieve required behavior without changing exchange-graphcache? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
@FreeRiderBysik Were you able to find a solution? I have the same situation and running into the same error. My use case is that a component triggers a sequence of mutations and I want to only run the last one through the cache exchange to trigger invalidation. Otherwise, the cache would be invalidated with every request which would be very wasteful for my app. Using Graphcache is not really an option for my app, we're generally happy with the simple approach of document caching. An alternative might be to debounce the cache invalidation, but that would require forking the cache exchange too as far as I understand. |
Beta Was this translation helpful? Give feedback.
-
Here is a solution: import { cacheExchange, CacheExchangeOpts } from '@urql/exchange-graphcache';
import {
Exchange,
ExchangeInput,
ExchangeIO,
Operation,
OperationResult,
} from 'urql';
import { empty, filter, merge, pipe, Source } from 'wonka';
import { getDocumentFieldName } from './utils';
interface WrappedCacheExchangeOpts extends CacheExchangeOpts {
cacheSkipOperations?: string[];
}
/** filter which operation will skip cache */
function filterOperationToSkipCache(
operation: Operation,
options?: WrappedCacheExchangeOpts
) {
const operationName = getDocumentFieldName(operation.query);
if (operationName && options?.cacheSkipOperations?.includes(operationName)) {
return true;
}
return false;
}
interface ResultsRef {
results: Source<OperationResult>;
}
function buildCacheExchangeWrapper(options: WrappedCacheExchangeOpts): Exchange {
const originalCacheExchange = cacheExchange(options);
return (input: ExchangeInput): ExchangeIO => {
return (operations) => {
const cacheSkippedOps = pipe(
operations,
filter((operation) => filterOperationToSkipCache(operation, options))
);
const cacheTargetOps = pipe(
operations,
filter((operation) => !filterOperationToSkipCache(operation, options))
);
const cacheSkipResults: ResultsRef = { results: empty };
return merge([
originalCacheExchange({
...input,
forward: (forwardedFromCacheExchangeOps) => {
const results = input.forward(
merge([forwardedFromCacheExchangeOps, cacheSkippedOps])
);
cacheSkipResults.results = pipe(
results,
filter((result) =>
filterOperationToSkipCache(result.operation, options)
)
);
return pipe(
results,
filter(
(result) =>
!filterOperationToSkipCache(result.operation, options)
)
);
},
})(cacheTargetOps),
cacheSkipResults.results,
]);
};
};
}
export default buildCacheExchangeWrapper; It can be really complicated to read, but it works fine. |
Beta Was this translation helpful? Give feedback.
Here is a solution: