Minimizing user-related dynamic data fetching in the API #1928

Closed
opened 2026-03-13 09:13:06 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @maelp on GitHub (Sep 15, 2025).

Question: Best way to handle dynamic partner data in sessions?

I’m building an app using BetterAuth for authentication.
I have a User object in the database, and each user is linked to a Partner object (which represents an organization).

The Partner object defines what the user has access to — for example, it has a list of accessible objects.
I want this information available in my session so I can easily check permissions.

Here’s the challenge:

  • Option 1: Store partner data in the session

    • When I create the session, I also load the partner and its accessible objects into it.
    • Problem: if the partner changes (e.g., permissions updated), I would need to refresh all active user sessions so they get the new data. This seems hard to manage.
  • Option 2: Load partner data on every request

    • Keep only the user ID (and partner ID) in the session.
    • On every API request, fetch the partner and accessible objects from the DB.
    • Problem: this means hundreds of requests may trigger DB lookups. I could add caching, but then I’d need a way to invalidate the cache when partner data changes.

What’s the best practice here?

  • Should I accept session invalidation as part of the model?
  • Or should I implement a cache layer for partner data, and only store IDs in the session?
  • How do people usually balance freshness vs performance in this situation?

Any advice would be appreciated!

Originally created by @maelp on GitHub (Sep 15, 2025). ### Question: Best way to handle dynamic partner data in sessions? I’m building an app using **BetterAuth** for authentication. I have a `User` object in the database, and each user is linked to a `Partner` object (which represents an organization). The `Partner` object defines what the user has access to — for example, it has a list of accessible objects. I want this information available in my session so I can easily check permissions. Here’s the challenge: - **Option 1: Store partner data in the session** - When I create the session, I also load the partner and its accessible objects into it. - Problem: if the partner changes (e.g., permissions updated), I would need to refresh all active user sessions so they get the new data. This seems hard to manage. - **Option 2: Load partner data on every request** - Keep only the user ID (and partner ID) in the session. - On every API request, fetch the partner and accessible objects from the DB. - Problem: this means hundreds of requests may trigger DB lookups. I could add caching, but then I’d need a way to invalidate the cache when partner data changes. What’s the best practice here? - Should I accept session invalidation as part of the model? - Or should I implement a cache layer for partner data, and only store IDs in the session? - How do people usually balance **freshness** vs **performance** in this situation? Any advice would be appreciated!
GiteaMirror added the question label 2026-03-13 09:13:06 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 15, 2025):

The best practice—both in BetterAuth and industry-wide—is to keep sessions slim: store only stable identifiers like userId and partnerId in the session, and fetch dynamic or permission-related partner data at request time. This avoids the complexity of invalidating sessions when partner data changes and ensures you always have fresh access control info. If you want to reduce DB load, implement an app-layer cache for partner data, and set up explicit cache invalidation when partner permissions change. BetterAuth’s session and custom session plugin mechanisms are designed for this pattern, letting you control what’s in the session and how/when to fetch fresh data, but they don’t provide automatic cache invalidation for related entities out of the box [custom session plugin code].

Session invalidation is best reserved for actual authentication/session changes, not for permission updates, unless you have a strong reason. If you need to force a session refresh (for example, after a user changes their active organization), you can use the refetch method from the useSession() hook [manual session refresh]. For most apps, fetching partner data per request (with caching if needed) is the most robust and maintainable approach.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 15, 2025): <!-- Answer --> The best practice—both in BetterAuth and industry-wide—is to keep sessions slim: store only stable identifiers like userId and partnerId in the session, and fetch dynamic or permission-related partner data at request time. This avoids the complexity of invalidating sessions when partner data changes and ensures you always have fresh access control info. If you want to reduce DB load, implement an app-layer cache for partner data, and set up explicit cache invalidation when partner permissions change. BetterAuth’s session and custom session plugin mechanisms are designed for this pattern, letting you control what’s in the session and how/when to fetch fresh data, but they don’t provide automatic cache invalidation for related entities out of the box [[custom session plugin code](https://github.com/better-auth/better-auth/blob/5b06218d7fd6673df2fdfbade1640d9bed0f2906/packages/better-auth/src/plugins/custom-session/index.ts)]. Session invalidation is best reserved for actual authentication/session changes, not for permission updates, unless you have a strong reason. If you need to force a session refresh (for example, after a user changes their active organization), you can use the `refetch` method from the `useSession()` hook [[manual session refresh](https://github.com/better-auth/better-auth/issues/3837)]. For most apps, fetching partner data per request (with caching if needed) is the most robust and maintainable approach. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/1bd862c5-d5b9-491b-b418-644521cf448c?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4668)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1928