[PR #8760] fix(client)!: restructure session fetch architecture #16442

Open
opened 2026-04-13 10:31:24 -05:00 by GiteaMirror · 0 comments
Owner

Original Pull Request: https://github.com/better-auth/better-auth/pull/8760

State: open
Merged: No


Summary

Replaces the dual-path session fetch (useAuthQuery signal-driven + fetchSessionWithRefresh direct) with a single unified fetchSession() owned by the session atom. This eliminates the double-fetch on every window focus event and preserves stable object references across refetches.

Root cause

The client had two independent fetch paths for session data that didn't coordinate:

  • Path A (useAuthQuery): subscribed to $sessionSignal, fetched on every flip
  • Path B (fetchSessionWithRefresh): fetched directly on focus/poll/online events, then flipped $sessionSignal, triggering Path A to fire a second fetch

What changed

  • equality.ts (new): isJsonEqual for JSON-safe deep equality; withEquality uses nanostores onSet with abort() to suppress atom updates when values are structurally identical
  • session-atom.ts (rewrite): session atom owns its fetch lifecycle via fetchSession() with AbortController dedup, fresh state reads at each async boundary (no stale closures), reference stabilization for data, and needsRefresh POST handling
  • session-refresh.ts (simplified): pure trigger coordinator; receives fetchSession instead of $fetch; makes no HTTP calls, no atom writes; subscribes to $sessionSignal via .listen() for auth-action-triggered refetches; dead cachedSession field removed
  • query.ts (improved): withEquality gate + reference stabilization in onSuccess for plugin queries (organization, passkey); fixed destructive .off() cleanup that killed all subscribers

Bugs fixed

  • Double-fetch on every window focus event
  • Stale closure: refresh manager captured atom state before async fetch, potentially overwriting newer data
  • Destructive .off() cleanup in useAuthQuery killed ALL atom subscribers instead of just one
  • Reference instability causing unnecessary React re-renders on identical session data

BREAKING CHANGE: SessionRefreshOptions no longer accepts sessionAtom or $fetch; normalizeSessionResponse is no longer exported.

**Original Pull Request:** https://github.com/better-auth/better-auth/pull/8760 **State:** open **Merged:** No --- ## Summary Replaces the dual-path session fetch (useAuthQuery signal-driven + fetchSessionWithRefresh direct) with a single unified `fetchSession()` owned by the session atom. This eliminates the double-fetch on every window focus event and preserves stable object references across refetches. ### Root cause The client had two independent fetch paths for session data that didn't coordinate: - **Path A** (`useAuthQuery`): subscribed to `$sessionSignal`, fetched on every flip - **Path B** (`fetchSessionWithRefresh`): fetched directly on focus/poll/online events, then flipped `$sessionSignal`, triggering Path A to fire a second fetch ### What changed - **`equality.ts` (new)**: `isJsonEqual` for JSON-safe deep equality; `withEquality` uses nanostores `onSet` with `abort()` to suppress atom updates when values are structurally identical - **`session-atom.ts` (rewrite)**: session atom owns its fetch lifecycle via `fetchSession()` with AbortController dedup, fresh state reads at each async boundary (no stale closures), reference stabilization for data, and `needsRefresh` POST handling - **`session-refresh.ts` (simplified)**: pure trigger coordinator; receives `fetchSession` instead of `$fetch`; makes no HTTP calls, no atom writes; subscribes to `$sessionSignal` via `.listen()` for auth-action-triggered refetches; dead `cachedSession` field removed - **`query.ts` (improved)**: `withEquality` gate + reference stabilization in `onSuccess` for plugin queries (organization, passkey); fixed destructive `.off()` cleanup that killed all subscribers ### Bugs fixed - Double-fetch on every window focus event - Stale closure: refresh manager captured atom state before async fetch, potentially overwriting newer data - Destructive `.off()` cleanup in `useAuthQuery` killed ALL atom subscribers instead of just one - Reference instability causing unnecessary React re-renders on identical session data BREAKING CHANGE: `SessionRefreshOptions` no longer accepts `sessionAtom` or `$fetch`; `normalizeSessionResponse` is no longer exported.
GiteaMirror added the pull-request label 2026-04-13 10:31:24 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#16442