Skip to content
Migrating from NextAuth.js v4? Read our migration guide.
API reference
@auth/drizzle-adapter

@auth/drizzle-adapter

Official Drizzle ORM adapter for Auth.js / NextAuth.js.

Installation

npm install drizzle-orm @auth/drizzle-adapter
npm install drizzle-kit --save-dev

DrizzleAdapter()

DrizzleAdapter<SqlFlavor>(db, schema?): Adapter

Create db instance and pass it to adapter. Add this adapter to your auth.ts Auth.js configuration object:

auth.ts
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import { DrizzleAdapter } from "@auth/drizzle-adapter"
import { db } from "./db.ts"
 
export const { handlers, auth } = NextAuth({
  adapter: DrizzleAdapter(db),
  providers: [
    Google,
  ],
})

Follow the Drizzle documentation for PostgreSQL setup, MySQL setup and SQLite setup.

If you want to use your own tables, you can pass them as a second argument. If you add non-nullable columns, make sure to provide a default value or rewrite functions to handle the missing values.

auth.ts
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"
import { DrizzleAdapter } from "@auth/drizzle-adapter"
import { accounts, sessions, users, verificationTokens } from "./schema"
import { db } from "./db.ts"
 
export const { handlers, auth } = NextAuth({
  adapter: DrizzleAdapter(db, { usersTable: users, accountsTable: accounts, sessionsTable: sessions, verificationTokensTable: verificationTokens }),
  providers: [
    Google,
  ],
})

Setup

First, create a schema that includes the minimum requirements for a next-auth adapter. You can select your favorite SQL flavor below and copy it. Additionally, you may extend the schema from the minimum requirements to suit your needs.

Postgres

schema.ts
import {
  timestamp,
  pgTable,
  text,
  primaryKey,
 integer
} from "drizzle-orm/pg-core"
import type { AdapterAccount } from '@auth/core/adapters'
import { randomUUID } from "crypto"
 
export const users = pgTable("user", {
 id: text("id").primaryKey().$defaultFn(() => randomUUID()),
 name: text("name"),
 email: text("email").notNull(),
 emailVerified: timestamp("emailVerified", { mode: "date" }),
 image: text("image"),
})
 
export const accounts = pgTable(
"account",
{
  userId: text("userId")
    .notNull()
    .references(() => users.id, { onDelete: "cascade" }),
  type: text("type").notNull(),
  provider: text("provider").notNull(),
  providerAccountId: text("providerAccountId").notNull(),
  refresh_token: text("refresh_token"),
  access_token: text("access_token"),
  expires_at: integer("expires_at"),
  token_type: text("token_type"),
  scope: text("scope"),
   id_token: text("id_token"),
  session_state: text("session_state"),
},
(account) => ({
  compoundKey: primaryKey({ columns: [account.provider, account.providerAccountId] }),
})
)
 
export const sessions = pgTable("session", {
 sessionToken: text("sessionToken").primaryKey(),
 userId: text("userId")
   .notNull()
   .references(() => users.id, { onDelete: "cascade" }),
 expires: timestamp("expires", { mode: "date" }).notNull(),
})
 
export const verificationTokens = pgTable(
 "verificationToken",
 {
   identifier: text("identifier").notNull(),
   token: text("token").notNull(),
   expires: timestamp("expires", { mode: "date" }).notNull(),
 },
 (vt) => ({
   compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
 })
)

MySQL

In MySQL, there’s no returning clause, so in the createUser function, we first insert a new user and then search by email to get the user’s data. To make the search faster, we suggest adding an index to the email column.

schema.ts
import {
 int,
 timestamp,
 mysqlTable,
 primaryKey,
 varchar,
} from "drizzle-orm/mysql-core"
import type { AdapterAccount } from "@auth/core/adapters"
 
export const users = mysqlTable("user", {
 id: varchar("id", { length: 255 }).primaryKey().$defaultFn(() => randomUUID()),
 name: varchar("name", { length: 255 }),
 email: varchar("email", { length: 255 }).notNull(),
 emailVerified: timestamp("emailVerified", { mode: "date", fsp: 3 }),
 image: varchar("image", { length: 255 }),
})
 
export const accounts = mysqlTable(
 "account",
  {
    userId: varchar("userId", { length: 255 })
       .notNull()
       .references(() => users.id, { onDelete: "cascade" }),
    type: varchar("type", { length: 255 }).notNull(),
    provider: varchar("provider", { length: 255 }).notNull(),
    providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(),
    refresh_token: varchar("refresh_token", { length: 255 }),
    access_token: varchar("access_token", { length: 255 }),
    expires_at: int("expires_at"),
    token_type: varchar("token_type", { length: 255 }),
    scope: varchar("scope", { length: 255 }),
    id_token: varchar("id_token", { length: 2048 }),
    session_state: varchar("session_state", { length: 255 }),
  },
  (account) => ({
    compoundKey: primaryKey({
        columns: [account.provider, account.providerAccountId],
      })
  })
)
 
export const sessions = mysqlTable("session", {
 sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(),
 userId: varchar("userId", { length: 255 })
   .notNull()
   .references(() => users.id, { onDelete: "cascade" }),
 expires: timestamp("expires", { mode: "date" }).notNull(),
})
 
export const verificationTokens = mysqlTable(
 "verificationToken",
 {
   identifier: varchar("identifier", { length: 255 }).notNull(),
   token: varchar("token", { length: 255 }).notNull(),
   expires: timestamp("expires", { mode: "date" }).notNull(),
 },
 (vt) => ({
   compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
 })
)

SQLite

schema.ts
import { integer, sqliteTable, text, primaryKey } from "drizzle-orm/sqlite-core"
import type { AdapterAccount } from "@auth/core/adapters"
 
export const users = sqliteTable("user", {
 id: text("id").primaryKey().$defaultFn(() => randomUUID()),
 name: text("name"),
 email: text("email").notNull(),
 emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
 image: text("image"),
})
 
export const accounts = sqliteTable(
 "account",
 {
   userId: text("userId")
     .notNull()
     .references(() => users.id, { onDelete: "cascade" }),
   type: text("type").notNull(),
   provider: text("provider").notNull(),
   providerAccountId: text("providerAccountId").notNull(),
   refresh_token: text("refresh_token"),
   access_token: text("access_token"),
   expires_at: integer("expires_at"),
   token_type: text("token_type"),
   scope: text("scope"),
   id_token: text("id_token"),
   session_state: text("session_state"),
 },
 (account) => ({
   compoundKey: primaryKey({
       columns: [account.provider, account.providerAccountId],
     })
 })
)
 
export const sessions = sqliteTable("session", {
 sessionToken: text("sessionToken").primaryKey(),
 userId: text("userId")
   .notNull()
   .references(() => users.id, { onDelete: "cascade" }),
 expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
})
 
export const verificationTokens = sqliteTable(
 "verificationToken",
 {
   identifier: text("identifier").notNull(),
   token: text("token").notNull(),
   expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
 },
 (vt) => ({
   compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
 })
)

Migrating your database

With your schema now described in your code, you’ll need to migrate your database to your schema.

For full documentation on how to run migrations with Drizzle, visit the Drizzle documentation or check how to apply changes directly to the database with push command.


Type parameters

Type parameter
SqlFlavor extends SqlFlavorOptions

Parameters

ParameterType
dbSqlFlavor
schema?DefaultSchema<SqlFlavor>

Returns

Adapter

Auth.js © Balázs Orbán and Team - 2024