cf-workers-query
v0.11.5
Published
Automatically cache and revalidate data in Cloudflare Workers. Using the Cache API and Execution Context
Downloads
2,203
Maintainers
Readme
Cf-Workers-Query
Automatically cache and revalidate data in Cloudflare Workers. Using the Cache API and waitUntil from cloudflare:workers.
Example
import { createQuery } from 'cf-workers-query';
const { data, error, invalidate } = await createQuery({
queryKey: ['user', userId],
queryFn: async () => {
const user = await fetchUser(userId);
return user;
},
gcTime: 60, // 60 seconds
});Stale Revalidation
import { createQuery } from 'cf-workers-query';
export default {
async fetch(request, env, ctx) {
const { data } = await createQuery({
queryKey: ['user', userId],
queryFn: async () => {
const user = await fetchUser(userId);
return user;
},
staleTime: 30, // 30 seconds
gcTime: 60, // 60 seconds
});
return new Response(JSON.stringify(data), {
headers: { 'content-type': 'application/json' },
});
},
};Background revalidation uses waitUntil from cloudflare:workers to keep the worker alive after responding.
Hono Example
import { cache } from 'cf-workers-query/hono';
app.get(
'/user/:id',
cache({
handler: async (ctx) => {
const user = await fetchUser(ctx.req.param('id'));
return ctx.json(user);
},
cacheKey: (ctx) => ['user', ctx.req.param('id')],
gcTime: 3600, // 1 hour in seconds
staleTime: 60, // 60 seconds
})
);Deduplication
By default, cf-workers-query uses a best-effort deduplication strategy via Cache API "processing markers". When multiple concurrent requests hit the same query key:
- Initial fetches: A brief (50ms) check is performed to see if another request already wrote the result to cache. If found, the cached result is returned instead of re-fetching.
- SWR background refreshes: If a refresh is already in progress for a key, additional refresh attempts are skipped entirely.
This is best-effort because CF Cache API has no atomic compare-and-swap operations. In practice, it reduces N concurrent executions to ~1-3.
Guaranteed Single-Flight with Durable Objects (Optional)
For use cases that require guaranteed single-flight execution, an optional Durable Object class is provided:
// worker entry
export { QueryDeduper } from 'cf-workers-query/durable-object';# wrangler.toml
[[durable_objects.bindings]]
name = "QUERY_DEDUPER"
class_name = "QueryDeduper"This requires Durable Objects billing and is opt-in.
API Reference
queryKey
Type: QueryKey | null
An optional key that uniquely identifies the query. Can be an array of values or a URL. If set to null, the query is not cached.
queryFn
Type: () => Promise<Data>
A function that returns a promise resolving with the data for the query.
staleTime
Type: number
Optional. The amount of time in seconds before the query data is considered stale. Default is 0.
gcTime
Type: number
Optional. The amount of time in seconds to keep data in the cache. Maps to Cache-Control: max-age. If 0 or falsy, caching is skipped entirely.
revalidate
Type: boolean
Optional. If true, the query will directly revalidate data (bypass stale check).
revalidateMode
Type: 'default' | 'probabilistic'
Optional. If 'probabilistic', uses a probability function to decide whether to revalidate stale data. Based on Cloudflare's cache stampede prevention.
retry
Type: number | ((failureCount: number, error: Error) => boolean)
Optional. Number of retry attempts on failure, or a function that receives the failure count and error and returns whether to retry.
retryDelay
Type: number | ((failureCount: number, error: Error) => number)
Optional. Delay between retries in milliseconds (note: unlike staleTime and gcTime which are in seconds, retryDelay is in milliseconds). Can be a fixed number or a function. Defaults to exponential backoff: Math.min(1000 * 2^attempt, 30000).
cacheName
Type: string
Optional. The name of the cache to use. Default is cf-workers-query-cache.
throwOnError
Type: boolean
Optional. If true, errors from queryFn are thrown instead of returned in the error field. Useful with Hono's error handling or when you want try/catch control flow.
enabled
Type: boolean | ((data: Data) => boolean)
Optional. Controls whether the query executes and/or caches. If false, the query is skipped. If a function, receives the fetched data and returns whether to cache it.
Credits
Inspired by TanStack Query but for Cloudflare Workers.
