Skip to main content

@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

Add the adapter to your pages/api/[...nextauth].ts next-auth configuration object.

pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { DrizzleAdapter } from "@auth/drizzle-adapter"
import { db } from "./schema"

export default NextAuth({
adapter: DrizzleAdapter(db),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
})
info

If you want to use your own tables, you can pass them as a second argument

pages/api/auth/[...nextauth].ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { DrizzleAdapter } from "@auth/drizzle-adapter"
import { db, accounts, sessions, users, verificationTokens } from "./schema"

export default NextAuth({
adapter: DrizzleAdapter(db, { usersTable: users, accountsTable: accounts, sessionsTable: sessions, verificationTokensTable: verificationTokens }),
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
],
})

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").unique(),
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) => ({
userIdIdx: index().on(account.userId),
compoundKey: primaryKey({ columns: [account.provider, account.providerAccountId] }),
})
)

export const sessions = pgTable("session", {
id: text("id").primaryKey().$defaultFn(() => randomUUID()),
sessionToken: text("sessionToken").notNull().unique(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
}, (session) => ({
userIdIdx: index().on(session.userId)
}))

export const verificationTokens = pgTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull().unique(),
expires: timestamp("expires", { mode: "date" }).notNull(),
},
(vt) => ({
compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
})
)

MySQL​

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 }).unique(),
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],
}),
userIdIdx: index('Account_userId_index').on(account.userId)
})
)

export const sessions = mysqlTable("session", {
id: varchar("id", { length: 255 }).primaryKey().$defaultFn(() => randomUUID()),
sessionToken: varchar("sessionToken", { length: 255 }).notNull().unique(),
userId: varchar("userId", { length: 255 })
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: timestamp("expires", { mode: "date" }).notNull(),
}, (session) => ({
userIdIdx: index('Session_userId_index').on(session.userId)
}))

export const verificationTokens = mysqlTable(
"verificationToken",
{
identifier: varchar("identifier", { length: 255 }).notNull(),
token: varchar("token", { length: 255 }).notNull().unique(),
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").unique(),
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],
}),
userIdIdx: index('Account_userId_index').on(account.userId)
})
)

export const sessions = sqliteTable("session", {
id: text("id").primaryKey().$defaultFn(() => randomUUID())
sessionToken: text("sessionToken").notNull().unique(),
userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
}, (table) => ({
userIdIdx: index('Session_userId_index').on(table.userId)
}))

export const verificationTokens = sqliteTable(
"verificationToken",
{
identifier: text("identifier").notNull(),
token: text("token").notNull().unique(),
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.


Type parameters​

β–ͺ SqlFlavor extends SqlFlavorOptions

Parameters​

β–ͺ db: SqlFlavor

β–ͺ schema?: DefaultSchema< SqlFlavor >

Returns​

Adapter