Skip to content

feat(universal-cache): add universal cache middleware package#1764

Open
lord007tn wants to merge 12 commits into
honojs:mainfrom
lord007tn:feat/universal-cache
Open

feat(universal-cache): add universal cache middleware package#1764
lord007tn wants to merge 12 commits into
honojs:mainfrom
lord007tn:feat/universal-cache

Conversation

@lord007tn

@lord007tn lord007tn commented Feb 24, 2026

Copy link
Copy Markdown

Summary

This PR introduces @hono/universal-cache as a new third-party middleware package for Hono.

It follows the direction discussed in honojs/hono#3857:
honojs/hono#3857

This middleware has been running internally in our production systems for a while. After validating it under real traffic and edge cases, we felt it was in a good place to open-source and get broader feedback on it.

Dependency Model

@hono/universal-cache depends on unstorage as the storage abstraction.

That gives us:

  • in-memory cache by default
  • adapter-based backends through unstorage drivers (Redis, KV, filesystem, and others)
  • one consistent cache API across runtimes

Major Features

  • response caching for Hono handlers via cacheMiddleware()
  • function-level caching via cacheFunction()
  • request-scoped defaults via cacheDefaults()
  • stale-while-revalidate (SWR)
  • in-flight deduplication for refresh paths
  • configurable cache keys (getKey) and integrity (integrity / hash)
  • custom serialize / deserialize hooks for response and function entries
  • cache control hooks: shouldBypassCache, shouldInvalidateCache, shouldRevalidate, validate
  • configurable HTTP methods and varies support for cache key variation
  • opt-in manual revalidation via revalidateHeader
  • keepPreviousOn5xx behavior for safer refresh failures
  • global helpers for cache defaults and storage instance management

Runtime Notes

  • default maxAge is 60
  • manual revalidation is disabled by default
  • on workerd, stale middleware entries refresh synchronously instead of relying on background self-fetch

Test Coverage

Added coverage for:

  • middleware caching behavior across methods
  • bypass / invalidation / manual revalidation flows
  • shouldRevalidate gating
  • SWR behavior in standard runtimes and workerd
  • vary-based keying
  • custom serialization / deserialization
  • function cache deduplication and validation
  • global defaults / storage helpers

Validation

  • corepack yarn eslint packages/universal-cache/src packages/universal-cache/vitest.config.ts packages/universal-cache/vitest.workerd.config.ts --cache --cache-location .cache/.eslintcache
  • corepack yarn workspace @hono/universal-cache typecheck
  • corepack yarn workspace @hono/universal-cache test --run
  • corepack yarn workspace @hono/universal-cache test:workerd --run
  • cd packages/universal-cache && bunx vitest run src/index.test.ts src/utils.test.ts

Related

Merge Note

Please merge this together with the docs PR above.

The author should do the following, if applicable

  • Add tests
  • Run tests
  • yarn changeset at the top of this repo and push the changeset
  • Follow the contribution guide

@changeset-bot

changeset-bot Bot commented Feb 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a4ebbfb

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

This PR includes changesets to release 1 package
Name Type
@hono/universal-cache Minor

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

@codecov

codecov Bot commented Feb 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.13%. Comparing base (70623e3) to head (ce27706).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1764   +/-   ##
=======================================
  Coverage   92.13%   92.13%           
=======================================
  Files         115      115           
  Lines        4106     4106           
  Branches     1072     1072           
=======================================
  Hits         3783     3783           
  Misses        287      287           
  Partials       36       36           
Flag Coverage Δ
oauth-providers 92.48% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@lord007tn

Copy link
Copy Markdown
Author

Addressed the Codecov coverage gaps with additional targeted tests.

What I added

  • New utility test suite:
    • packages/universal-cache/src/utils.test.ts
    • Covers all utils.ts branches and lines (normalizePathToName, stableStringify, TTL logic, expiry helpers).
  • Expanded index.test.ts with cache edge-case scenarios:
    • malformed path decoding fallback key path
    • cache entry rejection on integrity mismatch
    • cache entry rejection via validate()
    • rejection of cached headers containing etag: "undefined" / last-modified: "undefined"
    • invalidation behavior for non-cacheable no-store responses
    • function cache default key hashing path (without getKey)
    • removal of malformed function cache entries

Validation run

  • corepack yarn eslint packages/universal-cache/src --cache --cache-location .cache/.eslintcache
  • corepack yarn workspace @hono/universal-cache typecheck
  • corepack yarn workspace @hono/universal-cache test --run

All passed locally.

The new commit is: d79c864

Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/utils.ts Outdated
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts
@lord007tn
lord007tn force-pushed the feat/universal-cache branch from d79c864 to 89ea559 Compare March 12, 2026 10:47
@lord007tn
lord007tn force-pushed the feat/universal-cache branch from 89ea559 to 29d3e05 Compare April 28, 2026 02:59
@lord007tn

Copy link
Copy Markdown
Author

could you check it please
@yusukebe @usualoma

1 similar comment
@drag0n-app

Copy link
Copy Markdown

could you check it please
@yusukebe @usualoma

@drag0n-app

Copy link
Copy Markdown

@yusukebe @usualoma I really need this feature. Could you please review it? Thanks!

@yusukebe

Copy link
Copy Markdown
Member

@lord007tn

Sorry to be late. Can you merge the main branch into this branch to use the current stack (like pnpm instead of yarn) for honojs/middleware?

@lord007tn

Copy link
Copy Markdown
Author

@yusukebe Done,
I also merged the new tweaks that we have internally.

Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts
Comment thread packages/universal-cache/src/cache.ts
Comment thread packages/universal-cache/src/cache.ts Outdated
Comment thread packages/universal-cache/src/cache.ts Outdated
@lord007tn

Copy link
Copy Markdown
Author

@yusukebe done, fixed the remaining edge cases and pushed. all checks pass locally.

Comment thread packages/universal-cache/package.json Outdated
Comment thread packages/universal-cache/package.json Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants