Skip to content

feat(cache): add build_cache_key() for custom key builders - #307

Merged
allen0099 merged 1 commit into
masterfrom
feat/build-cache-key
Sep 27, 2026
Merged

allen0099 merged 1 commit into
masterfrom
feat/build-cache-key

Conversation

@allen0099

@allen0099 allen0099 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds build_cache_key(request, *components) (exported from fastapi_cachex) so a custom key_builder can add a dimension (user ID, tenant, locale) without rebuilding method|||host|||path|||query by hand.

from fastapi_cachex import build_cache_key

def per_user_key(request: Request) -> str:
    return build_cache_key(request, request.state.user_id)
  • Additive: with no components it returns exactly the previous default key (pinned by a test against the old formula for plain, query, escaped host/path, missing host and HEAD requests). default_key_builder stays public and returns build_cache_key(request).
  • Component typing: str | int. An int is written in decimal (1 and "1" are the same component). Anything else, including None and bool, raises TypeError, so a missing user ID cannot silently put every such caller under a "None" key. An empty string is a component of its own.
  • Escaping: every component goes through escape_key_component, so "a|||b" cannot line up with the components "a", "b".
  • clear_path: extra components follow the query string. Memory's key splitter used to split at most three times, so GET|||h|||/me||||||user counted as "has query params" and clear_path("/me") missed it; it now looks at the query component only. Redis now SCANs *|||path|||* and checks each returned key's third component and query in Python (a glob cannot tell an empty query followed by extras from a query). Side effect: a key where the path only appears as the host or an extra component is no longer cleared. Memcached is unchanged (exact-key only). A contract test runs on memory and live Redis.
  • Monitoring routes: /cached-hits and /cached-records records get a new extra_components list (decoded); query_params now holds only the query string. _parse_cache_key keeps its 4-tuple shape.
  • Docs: new "Adding components to the key" section in HTTP_CACHING.md, the "Authenticated endpoints" per-user example now uses build_cache_key, CACHE_FLOW.md points to it, API reference lists it; zh-TW mirrors updated.

Changelog

changelog.d/264.added.md

Test plan

  • uv run ruff check fastapi_cachex tests && uv run ruff format --check fastapi_cachex tests && uv run mypy fastapi_cachex --strict
  • Full suite including the live Redis and Memcached tests: 1148 passed, 1 skipped, coverage 99%
  • uv run pytest (no live servers): 957 passed, 192 skipped, coverage 94%

Closes #264

build_cache_key(request, *components) returns the default key unchanged
and appends each str/int component, escaped with escape_key_component, after
the query string. clear_path() on the memory and Redis backends matches such
keys by their path and no longer counts the extra components as a query
string; the monitoring routes report them in extra_components.

The per-user example in HTTP_CACHING.md now uses it.
@allen0099 allen0099 added this to the 0.3.9 milestone Sep 27, 2026
@allen0099
allen0099 added this pull request to stack #311 September 27, 2026 13:44
@allen0099
allen0099 merged commit f4161e1 into master Sep 27, 2026
12 checks passed
@allen0099
allen0099 deleted the feat/build-cache-key branch September 27, 2026 13:47
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.

@cache: public build_cache_key() helper so custom key builders don't hand-roll the format

1 participant