Interoperability with existing user database or other type of user storage #671

Closed
opened 2026-03-13 07:59:49 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @cbodin on GitHub (Feb 14, 2025).

Is this suited for github?

  • Yes, this is suited for github

Is your feature request related to a problem? Please describe.

I have a large system with an existing user base and i'm looking to use better-auth as an auth provider. A database with a migration strategy is already in place and i would like to use that as the storage for better-auth data.

When a new user is created i need to add them to my current database as i have lots of foreign keys, email messages and other functionality relying on this.

I do not want better-auth to manage migrations for me in my current database but i would still like a type-safe way of interacting with it without needing to double-check all the schemas in the documentation for every update. Sessions would preferable be stored in redis, rate limit and counter directly in memory.

The database in question is a mssql database (no adapter) and the columns cannot be mapped 1:1 with the ones better-auth expects.

I would also like to be able to do CI tests with my integartion to ensure the implementation is correct after library updates.

Describe the solution you'd like

Separating the database adapter into smaller, pluggable adapters per data type. The internal adapter is pretty close to how a public facing api could look like.

I'm also seeing a solution like this completely replacing the need for a secondary storage, as the session and rate limiting could be implemented using one adapter while the user and account uses another one.

Example basic usage:

const db = new Database("database.sqlite");

export const auth = betterAuth({
  ...sqliteDatabaseAdapter(db),
});

// equivalent to

export const auth = betterAuth({
  userAdapter: sqliteUserAdapter(db),
  sessionAdapter: sqliteSessionAdapter(db),
  accountAdapter: sqliteAccountAdapter(db),
  // ...additional adapters
});

Example with sessions in memory:

const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" });
const memoryDb = new Database(":memory:");

export const auth = betterAuth({
  ...postgresDatabaseAdapter(pgPool),
  sessionAdapter: sqliteSessionAdapter(memoryDb),
});

Example with sessions in memory, overwriting some of the session adapter methods:

const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" });
const memoryDb = new Database(":memory:");
const sessionAdapter = sqliteSessionAdapter(memoryDb);

export const auth = betterAuth({
  ...postgresDatabaseAdapter(pgPool),
  sessionAdapter: {
    ...sessionAdapter,
    createSession: (ctx, opts) => {
      // Alter before creation
      return sessionAdapter.createSession({
        ...opts,
        userAgent: ctx.request?.headers["x-mobile-client"] ?? opts.userAgent,
      });
    }
  }
});

Example using a pre-existing database for users, storing accounts in a postgres instance and keeping the rest in memory.

import db from "./drizzle.ts";

const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" });
const memoryDb = new Database(":memory:");

export const auth = betterAuth({
  ...sqliteDatabaseAdapter(memoryDb),
  accountAdapter: postgresAccountAdapter(memoryDb),
  userAdapter: {
    findUserByEmail: async (ctx, email) => {
      const user = await db.query.accounts.findFirst({
        columns: {
          identifier: true,
          created: true,
          updated: true,
        },
        with: {
          profile: {
            columns: {
              first_name: true,
              last_name: true,
            }
          }
        }
      });

      return user ? {
        id: user.identifier,
        createdAt: new Date(user.created * 1000),
        updatedAt: new Date(user.updated * 1000),
        name: `${user.first_name} ${user.last_name}`
      } : null;
    }
  },
});

Describe alternatives you've considered

Using a custom database adapter, using the model argument to interact with the correct data source.

Additional context

This issue is mainly opened to start a discussion, and the proposed solution something to discuss around.

I can see this lowering the threshold to implement adapters for different database engines, and possibly open up for more restricted storage options as the adapter would not have to implement a complete query interface but rather specific functions to handle an exact use case.

Depending on how the adapters are implemented, this could probably solve:

Defining a stricter interface for interacting with the backend would also allow for much easier testing, using e.g. testcontainers to verify against a real database that the adapter functions exactly as expected. The adapter test would only need to be written once, then tested against every type of adapter.

This would of course have great implications on the current migrations and additional fields functionality and probably loads more.

Edit:
This would enable a custom implementation of password-based accounts, eliminating the need for an accounts table when social login is not used. It also simplifies the adoption of better-auth in systems where passwords are stored in the same table as user data.

Originally created by @cbodin on GitHub (Feb 14, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. I have a large system with an existing user base and i'm looking to use better-auth as an auth provider. A database with a migration strategy is already in place and i would like to use that as the storage for better-auth data. When a new user is created i need to add them to my current database as i have lots of foreign keys, email messages and other functionality relying on this. I do not want better-auth to manage migrations for me in my current database but i would still like a type-safe way of interacting with it without needing to double-check all the schemas in the documentation for every update. Sessions would preferable be stored in redis, rate limit and counter directly in memory. The database in question is a mssql database (no adapter) and the columns cannot be mapped 1:1 with the ones better-auth expects. I would also like to be able to do CI tests with my integartion to ensure the implementation is correct after library updates. ### Describe the solution you'd like Separating the database adapter into smaller, pluggable adapters per data type. The internal adapter is pretty close to how a public facing api could look like. I'm also seeing a solution like this completely replacing the need for a secondary storage, as the session and rate limiting could be implemented using one adapter while the user and account uses another one. Example basic usage: ```ts const db = new Database("database.sqlite"); export const auth = betterAuth({ ...sqliteDatabaseAdapter(db), }); // equivalent to export const auth = betterAuth({ userAdapter: sqliteUserAdapter(db), sessionAdapter: sqliteSessionAdapter(db), accountAdapter: sqliteAccountAdapter(db), // ...additional adapters }); ``` Example with sessions in memory: ```ts const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" }); const memoryDb = new Database(":memory:"); export const auth = betterAuth({ ...postgresDatabaseAdapter(pgPool), sessionAdapter: sqliteSessionAdapter(memoryDb), }); ``` Example with sessions in memory, overwriting some of the session adapter methods: ```ts const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" }); const memoryDb = new Database(":memory:"); const sessionAdapter = sqliteSessionAdapter(memoryDb); export const auth = betterAuth({ ...postgresDatabaseAdapter(pgPool), sessionAdapter: { ...sessionAdapter, createSession: (ctx, opts) => { // Alter before creation return sessionAdapter.createSession({ ...opts, userAgent: ctx.request?.headers["x-mobile-client"] ?? opts.userAgent, }); } } }); ``` Example using a pre-existing database for users, storing accounts in a postgres instance and keeping the rest in memory. ```ts import db from "./drizzle.ts"; const pgDb = new Pool({ connectionString: "postgres://user:password@localhost:5432/database" }); const memoryDb = new Database(":memory:"); export const auth = betterAuth({ ...sqliteDatabaseAdapter(memoryDb), accountAdapter: postgresAccountAdapter(memoryDb), userAdapter: { findUserByEmail: async (ctx, email) => { const user = await db.query.accounts.findFirst({ columns: { identifier: true, created: true, updated: true, }, with: { profile: { columns: { first_name: true, last_name: true, } } } }); return user ? { id: user.identifier, createdAt: new Date(user.created * 1000), updatedAt: new Date(user.updated * 1000), name: `${user.first_name} ${user.last_name}` } : null; } }, }); ``` ### Describe alternatives you've considered Using a custom database adapter, using the `model` argument to interact with the correct data source. ### Additional context This issue is mainly opened to start a discussion, and the proposed solution something to discuss around. I can see this lowering the threshold to implement adapters for different database engines, and possibly open up for more restricted storage options as the adapter would not have to implement a complete query interface but rather specific functions to handle an exact use case. Depending on how the adapters are implemented, this could probably solve: - https://github.com/better-auth/better-auth/issues/1298 - https://github.com/better-auth/better-auth/issues/902 - https://github.com/better-auth/better-auth/issues/785 Defining a stricter interface for interacting with the backend would also allow for much easier testing, using e.g. [testcontainers](https://node.testcontainers.org/quickstart/) to verify against a real database that the adapter functions exactly as expected. The adapter test would only need to be written once, then tested against every type of adapter. This would of course have great implications on the current migrations and additional fields functionality and probably loads more. **Edit:** This would enable a custom implementation of password-based accounts, eliminating the need for an accounts table when social login is not used. It also simplifies the adoption of better-auth in systems where passwords are stored in the same table as user data.
Author
Owner

@dosubot[bot] commented on GitHub (Jun 14, 2025):

Hi, @cbodin. I'm Dosu, and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • You raised an issue about integrating better-auth with an MSSQL user database without using it for database migrations.
  • Proposed developing pluggable adapters for type-safe interactions and integration.
  • Suggested separate adapters for sessions and rate limiting, possibly stored in memory.
  • No comments or further activity have been made on this issue.

Next Steps:

  • Please let me know if this issue is still relevant to the latest version of the better-auth repository by commenting here.
  • If there is no response, the issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

@dosubot[bot] commented on GitHub (Jun 14, 2025): Hi, @cbodin. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog. I'm marking this issue as stale. **Issue Summary:** - You raised an issue about integrating better-auth with an MSSQL user database without using it for database migrations. - Proposed developing pluggable adapters for type-safe interactions and integration. - Suggested separate adapters for sessions and rate limiting, possibly stored in memory. - No comments or further activity have been made on this issue. **Next Steps:** - Please let me know if this issue is still relevant to the latest version of the better-auth repository by commenting here. - If there is no response, the issue will be automatically closed in 7 days. Thank you for your understanding and contribution!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#671