Back to Better Auth

Drizzle ORM Adapter

docs/content/docs/adapters/drizzle.mdx

1.7.09.4 KB
Original Source

Drizzle ORM is a powerful and flexible ORM for Node.js and TypeScript. It provides a simple and intuitive API for working with databases, and supports a wide range of databases including MySQL, PostgreSQL, SQLite, and more.

Before getting started, make sure you have Drizzle installed and configured. For more information, see Drizzle Documentation

Installation

To use the Drizzle adapter, you need to install the @better-auth/drizzle-adapter package:

package-install
@better-auth/drizzle-adapter

Example Usage

You can use the Drizzle adapter to connect to your database as follows.

ts
import { betterAuth } from "better-auth";
import { drizzleAdapter } from "@better-auth/drizzle-adapter";
import { db } from "./database.ts";

export const auth = betterAuth({
  database: drizzleAdapter(db, { // [!code highlight]
    provider: "sqlite", // or "pg" or "mysql" // [!code highlight]
  }), // [!code highlight]
  //... the rest of your config
});

Schema generation & migration

The Better Auth CLI allows you to generate or migrate your database schema based on your Better Auth configuration and plugins.

To generate the schema required by Better Auth, run the following command:

package-install
npx auth@latest generate

To generate and apply the migration, run the following commands:

<Tabs items={["generate", "migrate"]}> <Tab value="generate"> package-install npx drizzle-kit generate # generate the migration file </Tab>

<Tab value="migrate"> ```package-install npx drizzle-kit migrate # apply the migration ``` </Tab> </Tabs>

Joins

Database joins are useful when Better-Auth needs to fetch related data from multiple tables in a single query. Endpoints like /get-session, /get-full-organization and many others benefit greatly from this feature, seeing upwards of 2x to 3x performance improvements depending on database latency.

The Drizzle adapter supports joins out of the box since version 1.4.0. To enable this feature, set advanced.database.joins to true in your auth configuration.

ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  advanced: {
    database: {
      joins: true,
    },
  },
});
<Callout type="warn"> Please make sure that your Drizzle schema has the necessary relations defined. If you do not see any relations in your Drizzle schema, you can manually add them using the [`relation`](https://orm.drizzle.team/docs/relations) drizzle-orm function or run our latest CLI version `npx auth@latest generate` to generate a new Drizzle schema with the relations.

Additionally, you're required to pass each relation through the drizzle adapter schema object. </Callout>

When a table has multiple foreign keys to the same table, each relation pair must use a matching relationName. The CLI generates these names automatically. If you generated your schema with an older CLI, regenerate it or add matching names to both sides.

The relationName prefix follows your table naming: with usePlural: true it is plural (tests_userId), otherwise singular (test_userId). Keep both sides identical.

ts
export const usersRelations = relations(users, ({ many }) => ({
  testsByUserId: many(tests, { relationName: "tests_userId" }),
  testsByManagerId: many(tests, { relationName: "tests_managerId" }),
}));

export const testsRelations = relations(tests, ({ one }) => ({
  user: one(users, {
    fields: [tests.userId],
    references: [users.id],
    relationName: "tests_userId",
  }),
  manager: one(users, {
    fields: [tests.managerId],
    references: [users.id],
    relationName: "tests_managerId",
  }),
}));

Do not keep both singular and plural aliases for the same foreign key (for example, both user and users). Drizzle treats those as separate relations and cannot infer which reverse relation a join should use.

Modifying Table Names

The Drizzle adapter expects the schema you define to match the table names. For example, if your Drizzle schema maps the user table to users, you need to manually pass the schema and map it to the user table.

ts
import { betterAuth } from "better-auth";
import { db } from "./drizzle";
import { drizzleAdapter } from "@better-auth/drizzle-adapter";
import { schema } from "./schema";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "sqlite", // or "pg" or "mysql"
    schema: { // [!code highlight]
      ...schema, // [!code highlight]
      user: schema.users, // [!code highlight]
    }, // [!code highlight]
  }),
});

You can either modify the provided schema values like the example above, or you can mutate the auth config's modelName property directly. For example:

ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "sqlite", // or "pg" or "mysql"
    schema,
  }),
  user: {
    modelName: "users", // [!code highlight]
  }
});

Modifying Field Names

We map field names based on property you passed to your Drizzle schema. For example, if you want to modify the email field to email_address, you simply need to change the Drizzle schema to:

ts
export const user = mysqlTable("user", {
  // Changed field name without changing the schema property name
  // This allows drizzle & better-auth to still use the original field name,
  // while your DB uses the modified field name
  email: varchar("email_address", { length: 255 }).notNull().unique(), // [!code highlight]
  // ... others
});

You can either modify the Drizzle schema like the example above, or you can mutate the auth config's fields property directly. For example:

ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "sqlite", // or "pg" or "mysql"
    schema,
  }),
  user: {
    fields: {
      email: "email_address", // [!code highlight]
    }
  }
});

Using Plural Table Names

If all your tables are using plural form, you can just pass the usePlural option:

ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    ...
    usePlural: true, // [!code highlight]
  }),
});

Custom Schema namespace

If you're using PostgreSQL and you want to generate the schema with a custom schema namespace, you can pass the schemaName option to the Drizzle adapter.

ts
export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
    schemaName: "auth", // [!code highlight]
  }),
});

Then when using the Better Auth CLI, it will generate the schema that looks something like this:

bash
npx @better-auth/cli@latest generate
ts
export const authSchema = pgSchema("auth");

export const user = authSchema.table("user", {...});
export const session = authSchema.table("session", {...});

The schemaName option is also supported by the @better-auth/drizzle-adapter/relations-v2 adapter described below.

Drizzle Relations v2

The current Drizzle adapter uses Drizzle Relations v1. To use Drizzle Relations v2, you need to use the @better-auth/drizzle-adapter/relations-v2 adapter.

Install the adapter:

package-install
npm install @better-auth/drizzle-adapter

Update your imports to use the relations-v2 adapter:

ts
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from '@better-auth/drizzle-adapter/relations-v2'; // [!code highlight]
import { db } from './database.ts';
import * as schema from './schema.ts';

export const auth = betterAuth({
	database: drizzleAdapter(db, {
		provider: 'sqlite', // or "pg" or "mysql"
		schema,
	}),
	//... the rest of your config
});

Then regenerate your schema using the Better Auth CLI:

package-install
npx auth@latest generate
<Callout type="info"> You do not need to run database migrations when upgrading to Relations v2. The database structure remains the same - only the relation definitions change. The schema generator will output the new v2 format automatically. </Callout>

The generated auth schema exports relations using defineRelationsPart, which is designed to be merged alongside your app's own defineRelations. Pass both to the drizzle instance — schema is no longer required since Drizzle v1 RC:

ts
import { drizzle } from 'drizzle-orm/...';
// generated relations from auth CLI (uses defineRelationsPart)
import { authRelations } from './auth-schema.ts';
// your app's own relations (uses defineRelations)
import { relations } from './app-schema.ts';

export const db = drizzle({
	client,
	// authRelations uses defineRelationsPart, // [!code highlight]
	// so it must come after the main relations // [!code highlight]
	relations: { ...relations, ...authRelations }, // [!code highlight]
});
<Callout type="info"> `defineRelationsPart` is a partial relation definition that must be spread after full `defineRelations` entries. See the [Drizzle docs on relation parts](https://orm.drizzle.team/docs/relations-v2#relations-parts) for details. </Callout>

Additional Information

  • If you're looking for performance improvements or tips, take a look at our guide to <Link href="/docs/guides/optimizing-for-performance">performance optimizations</Link>.