Query Parameters
By default no query parameter is part of the cache key, and none reaches the handler: /swatch, ?color=red, and ?color=red&utm=x all share one entry. Keying on the full query is what shatters a cache into countless entries — tracking tags and cache busters alone can destroy the hit rate — so query parameters are opt-in.
allowQuery is that opt-in, as an allowlist. Only the listed parameters vary the key and reach the handler; everything else is ignored:
const handler = defineCachedHandler(
async (event) => {
const url = event.url ?? new URL(event.req.url);
const color = url.searchParams.get("color") ?? "default";
return new Response(renderSwatch(color), {
headers: { "content-type": "text/html" },
});
},
{
maxAge: 300,
allowQuery: ["color"],
},
);
// All of these share ONE cached entry (only `color` matters):
await handler({ req: new Request("https://shop.dev/swatch?color=red") });
await handler({ req: new Request("https://shop.dev/swatch?color=red&lang=en") });
await handler({ req: new Request("https://shop.dev/swatch?color=red&utm=ig") });Matching is normalized and order-independent: ?color=red&size=l and ?size=l&color=red share an entry, and repeated array parameters such as ?color=red&color=blue match in any order.
Tip
Ignored parameters never reach the handler. Parameters outside the allowlist are stripped from the URL the handler receives — just like undeclared cookies and undeclared headers. A handler therefore cannot accidentally render output from a parameter the key does not cover.
Important
A handler that reads url.searchParams without allowQuery sees an empty query. Declare every parameter the output depends on.
allowQuery adds nothing to the response Vary header: query parameters live in the URL, and downstream caches already key on the URL.
#Keying the full query
allowQuery: true puts the whole query string in the key and passes it to the handler unchanged:
defineCachedHandler(myHandler, {
maxAge: 300,
allowQuery: true,
});Use it for a route whose parameters are not known ahead of time, such as a proxy or a search endpoint. Every distinct query becomes its own entry, including tracking parameters, so prefer an allowlist where the parameter names are known.
#Edge cases
allowQuery: []andallowQuery: falseare the default. Both remove the whole query string, as an unset option does. Every request to the path shares one entry, and the handler always receives a query-less URL.Names are case-sensitive.
allowQuery: ["color"]does not match?Color=red.getKeyoverrides the key, not the filtering. A customgetKeydecides the entire cache key, andallowQueryno longer affects it — but disallowed parameters are still stripped from the URL the handler receives:defineCachedHandler(myHandler, { maxAge: 300, allowQuery: ["color"], // still strips other params from the handler's URL getKey: (event) => new URL(event.req.url).pathname, // but this defines the key });