Skip to content

fix(cache): send the headers and cookies dependencies set on every response (#233) - #435

Merged
allen0099 merged 1 commit into
masterfrom
fix/233-dependency-headers
Oct 2, 2026
Merged

allen0099 merged 1 commit into
masterfrom
fix/233-dependency-headers

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #233.

FastAPI hands every dependency and the handler one sub-Response, and merges it into the result only when the endpoint returns plain data. @cache always returns a Response, so before this change:

  • a handler without response: Response lost every header and cookie its dependencies set;
  • a handler with it stored the dependencies' headers with the entry and replayed the values of the request that filled the cache (a rate-limit countdown answered 10, 10, 10).

What changes

  • When the handler declares no Response parameter, @cache injects a keyword-only __cachex_response. At wrapper entry the dependencies have already run, so the sub-response lines at that point are the dependencies' lines. Only the lines the handler adds after that are stored.
  • The dependencies' current lines are added to every response: miss, hit and 304.
  • On a cacheable GET response:
    • A dependency's cookie, or a dependency's private / no-store Cache-Control, counts as if the handler had set it. The response is not stored, and it is sent with that header or with private.
    • A dependency's other Cache-Control is handled like the handler's own: the decorator's replaces it, and a bare @cache() keeps it (@cache() with no directives sends an empty Cache-Control header #363).
    • When the handler sets a header a dependency also set, the handler's value is sent, on a hit too.
    • A status code a dependency sets is sent but keeps the response out of the backend, because it may apply to that request only.
  • Non-GET and uncacheable responses get every dependency line as FastAPI would send them.
  • Annotated[Response, Depends(...)] and response: Response = Depends(...) are dependency results, not the sub-response, so the injection still happens for them.
  • get_response keeps its signature as a compatibility wrapper.

Behaviour changes (233.changed.md)

  • A dependency that sets a cookie on every request (an app-wide CSRF or session-refresh dependency, say) now keeps every @cache route it applies to out of the backend. The docs add a note on this.
  • @cache also adds the dependencies' headers to a Response the handler returns itself. FastAPI does not do this.
  • A handler that deletes a dependency's header removes it only on the requests where the handler runs.
  • Entries stored before the upgrade may still replay dependency headers until their TTL ends.

Review

An independent read-only review ran twice:

  • The first pass found that a dependency's private header no longer prevented storage. That is fixed and tested.
  • The second pass found no blocker. Its findings are fixed:
    • The changelog wording about cookies before 0.4.1 was wrong.
    • A dependency-set status could be replayed from the cache.
    • The default-value Depends form was taken for the sub-response.
    • A bare @cache() dropped a dependency's Cache-Control.
    • Two checks had no test.

Each new test was mutation-checked: reverting the code it guards makes it fail.

Checks

  • pre-commit
  • mypy tests and mypy scripts
  • the full suite with live Redis and Memcached: 1816 passed, coverage 99.84%
  • both zensical builds with --strict

…sponse (#233)

FastAPI merges the shared sub-Response into the result only when the
endpoint returns plain data, and the @cache wrapper always returns a
Response. A dependency's headers and cookies were lost on a handler
without a `response: Response` parameter; with one, they were stored with
the entry and replayed on every hit with the values of the request that
filled the cache.

The wrapper now always receives the sub-response (it injects the
parameter when the handler does not declare one) and records the lines
the dependencies set before the handler runs. Only the lines the handler
adds are part of its response and stored; the dependencies' lines are
added to every response the wrapper sends, miss, hit and 304. A cookie a
dependency sets keeps the response out of the backend and makes it
private, as a cookie the handler sets does.
@allen0099 allen0099 added this to the 0.4.1 milestone Oct 2, 2026
@allen0099 allen0099 added bug Something isn't working http-cache The @cache decorator, cache keys and Cache-Control handling labels Oct 2, 2026
@allen0099
allen0099 merged commit 14bf2ee into master Oct 2, 2026
17 checks passed
@allen0099
allen0099 deleted the fix/233-dependency-headers branch October 2, 2026 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working http-cache The @cache decorator, cache keys and Cache-Control handling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@cache drops or replays stale headers and cookies set on the shared Response

1 participant