Skip to content

feat(router): SSR and routeLoader eTag and cacheKey handling#8699

Merged
wmertens merged 2 commits into
build/v2from
auto-etag
Jun 9, 2026
Merged

feat(router): SSR and routeLoader eTag and cacheKey handling#8699
wmertens merged 2 commits into
build/v2from
auto-etag

Conversation

@wmertens

@wmertens wmertens commented Jun 5, 2026

Copy link
Copy Markdown
Member

Adds caching and eTag support to routeloaders, and adds auto eTag generation for SSR.

Breaking change if using previous beta: the cacheKey callback arguments changed, now you get the requestEv.

Changes:

  • Split caching into separate SSR and loader caches (LRU) and add clearLoaderCache() alongside clearSsrCache().
  • Introduce shared ETag utilities (normalizeETag, hash, performETagMatch) and apply normalized ETags to cacheKey callbacks while quoting only for HTTP headers.

Copilot AI review requested due to automatic review settings June 5, 2026 18:20
@wmertens wmertens requested review from a team as code owners June 5, 2026 18:20
@changeset-bot

changeset-bot Bot commented Jun 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 2c12f9f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@qwik.dev/router Major
eslint-plugin-qwik Major
@qwik.dev/core Major
create-qwik Major
@qwik.dev/react Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@maiieul maiieul moved this from Backlog to Waiting For Review in Qwik Development Jun 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends Qwik Router’s server-side caching story by adding in-memory caching + ETag-based conditional request handling for both SSR HTML and routeLoader$ JSON responses, including automatic ETag generation (hash-based) when caching is enabled but no explicit ETag is provided.

Changes:

  • Split caching into separate SSR and loader caches (LRU) and add clearLoaderCache() alongside clearSsrCache().
  • Introduce shared ETag utilities (normalizeETag, quoteETag, hashETag, checkETagMatch) and apply normalized/unquoted ETags to cacheKey callbacks while quoting only for HTTP headers.
  • Update public types/docs: CacheKeyFn now takes (requestEv, eTag) and loaders gain a cacheKey option while eTag: true is removed in favor of cacheKey.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/qwik-router/src/ssg/resolve-request-handlers-ssg.ts Wires new SSR/loader cache deps into SSG request handler resolver.
packages/qwik-router/src/runtime/src/types.ts Updates public types/docs for ETag + cacheKey behavior; adds loader cacheKey support.
packages/qwik-router/src/runtime/src/route-loaders.unit.ts Updates loader test helper to include __cacheKey.
packages/qwik-router/src/runtime/src/route-loaders.ts Parses/stores new loader cacheKey option; exports filterSearchParams.
packages/qwik-router/src/runtime/src/qwik-router.runtime.api.md Updates extracted API type for CacheKeyFn.
packages/qwik-router/src/runtime/src/not-found-wrapper.ts Updates synthetic module cacheKey impl to new CacheKeyFn signature.
packages/qwik-router/src/middleware/request-handler/resolve-request-handlers.ts Wires new SSR/loader cache deps into runtime request handler resolver.
packages/qwik-router/src/middleware/request-handler/resolve-request-handlers-core.ts Implements SSR cache hit/miss flow with optional auto-hashed ETag and new cache key resolution.
packages/qwik-router/src/middleware/request-handler/middleware.request-handler.api.md Adds clearLoaderCache() to public middleware API docs.
packages/qwik-router/src/middleware/request-handler/index.ts Exposes clearLoaderCache() from the middleware entrypoint.
packages/qwik-router/src/middleware/request-handler/handlers/loader-handler.unit.ts Adds tests for normalized/unquoted eTag passed to cacheKey callback + quoted response header.
packages/qwik-router/src/middleware/request-handler/handlers/loader-handler.ts Adds loader JSON caching + ETag handling (explicit and auto-hashed) and cache-key support.
packages/qwik-router/src/middleware/request-handler/etag.ts Refactors caching into LRU caches for SSR and loaders; adds default key builders + clear functions.
packages/qwik-router/src/middleware/request-handler/etag-hash.unit.ts Adds unit tests for ETag quoting/normalization/hashing and If-None-Match matching behavior.
packages/qwik-router/src/middleware/request-handler/etag-hash.ts Adds shared ETag normalization/quoting/hashing and conditional request helper.
packages/docs/src/routes/docs/(qwikrouter)/caching/index.mdx Documents SSR and loader caching behavior and new APIs.
packages/docs/src/routes/api/qwik-router/index.mdx Updates generated API docs for CacheKeyFn and ContentModuleETag.
packages/docs/src/routes/api/qwik-router/api.json Updates generated API JSON for CacheKeyFn and ContentModuleETag.
packages/docs/src/routes/api/qwik-router-middleware-request-handler/index.mdx Documents clearLoaderCache() in generated middleware API docs.
packages/docs/src/routes/api/qwik-router-middleware-request-handler/api.json Adds clearLoaderCache() to generated middleware API JSON.
.changeset/lucky-pugs-marry.md Changeset entry for SSR auto-ETag hashing when caching without explicit eTag.
.changeset/free-poems-serve.md Changeset entry for loader caching + ETag support.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/qwik-router/src/runtime/src/types.ts
Comment thread packages/qwik-router/src/runtime/src/types.ts Outdated
Comment thread packages/docs/src/routes/docs/(qwikrouter)/caching/index.mdx
@pkg-pr-new

pkg-pr-new Bot commented Jun 5, 2026

Copy link
Copy Markdown

Open in StackBlitz

@qwik.dev/core

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/core@8699

@qwik.dev/router

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/router@8699

eslint-plugin-qwik

npm i https://pkg.pr.new/QwikDev/qwik/eslint-plugin-qwik@8699

create-qwik

npm i https://pkg.pr.new/QwikDev/qwik/create-qwik@8699

@qwik.dev/optimizer

npm i https://pkg.pr.new/QwikDev/qwik/@qwik.dev/optimizer@8699

commit: 2c12f9f

@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor
built with Refined Cloudflare Pages Action

⚡ Cloudflare Pages Deployment

Name Status Preview Last Commit
qwik-docs ✅ Ready (View Log) Visit Preview 2c12f9f

@wmertens wmertens force-pushed the auto-etag branch 2 times, most recently from 7fba4a6 to f55c29d Compare June 8, 2026 16:50

@Varixo Varixo left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment on lines +48 to +95
function createLoaderRequestEvent(
requestEv: RequestEventInternal,
filteredSearch: string,
getLoaderRequestEvent: LoaderRequestEventFactory
): RequestEventInternal {
const url = new URL(requestEv.url);
url.search = filteredSearch;

return Object.create(requestEv, {
cookie: {
value: requestEv.cookie,
enumerable: true,
},
pathname: {
get: () => url.pathname,
enumerable: true,
},
query: {
get: () => url.searchParams,
enumerable: true,
},
request: {
value: createRequestUrlView(requestEv.request, url),
enumerable: true,
},
sharedMap: {
value: requestEv.sharedMap,
enumerable: true,
},
url: {
value: url,
enumerable: true,
},
resolveValue: {
value: (async (loaderOrAction: LoaderInternal | ActionInternal) => {
if (loaderOrAction.__brand === 'server_loader') {
const loaderValues = getRouteLoaderValues(requestEv);
if (loaderOrAction.__id in loaderValues) {
return loaderValues[loaderOrAction.__id];
}
return loadRouteLoader(loaderOrAction, getLoaderRequestEvent(loaderOrAction));
}
return undefined;
}) as RequestEventInternal['resolveValue'],
enumerable: true,
},
}) as RequestEventInternal;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we had something like this somewhere, no?

- routeLoaders are now also cacheable
- eTag is auto generated from content hash when caching
- make cacheKey and eTag always string, empty when not set
- when `qwikRouterOptions.strictLoaders`, loader requestEv is scoped to own path
- when `loaderOptions.search`, loader requestEv is scoped to own search
- disable redirects for loader requests
@wmertens wmertens merged commit 1ec6f51 into build/v2 Jun 9, 2026
21 of 23 checks passed
@wmertens wmertens deleted the auto-etag branch June 9, 2026 16:14
@github-project-automation github-project-automation Bot moved this from Waiting For Review to Done in Qwik Development Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

4 participants