Drizzle
gau supports Drizzle ORM 1.0, starting with 1.0.0-rc.4, for SQLite and PostgreSQL. Drizzle 0.x is no longer supported. This guide uses Turso for SQLite; Cloudflare D1 uses the same schema with a different client.
Email login also needs a verification table, passed as the fourth argument to DrizzleAdapter.
-
Install Dependencies
Section titled “Install Dependencies”Terminal window npm i drizzle-orm@1.0.0-rc.4 @libsql/clientnpm i -D drizzle-kit@1.0.0-rc.4Terminal window pnpm add drizzle-orm@1.0.0-rc.4 @libsql/clientpnpm add -D drizzle-kit@1.0.0-rc.4Terminal window yarn add drizzle-orm@1.0.0-rc.4 @libsql/clientyarn add -D drizzle-kit@1.0.0-rc.4Terminal window bun add drizzle-orm@1.0.0-rc.4 @libsql/clientbun add -D drizzle-kit@1.0.0-rc.4Terminal window ni drizzle-orm@1.0.0-rc.4 @libsql/clientni -D drizzle-kit@1.0.0-rc.4Terminal window npm i drizzle-orm@1.0.0-rc.4 pgnpm i -D drizzle-kit@1.0.0-rc.4Terminal window pnpm add drizzle-orm@1.0.0-rc.4 pgpnpm add -D drizzle-kit@1.0.0-rc.4Terminal window yarn add drizzle-orm@1.0.0-rc.4 pgyarn add -D drizzle-kit@1.0.0-rc.4Terminal window bun add drizzle-orm@1.0.0-rc.4 pgbun add -D drizzle-kit@1.0.0-rc.4Terminal window ni drizzle-orm@1.0.0-rc.4 pgni -D drizzle-kit@1.0.0-rc.4 -
Define the Database Schema
Section titled “Define the Database Schema”Define the user and account fields shown below.
sessionStateand the account’screatedAtare optional. You can add application columns and an optional textrolecolumn to the user table. Table names are up to you; keep the TypeScript field names shown here.snakeCase.table()maps fields such asemailVerifiedto SQL columns such asemail_verified. User IDs and account identifiers must be non-null strings. User timestamps must use columns that accept JavaScriptDatevalues.schema.ts import { integer, primaryKey, snakeCase, text } from 'drizzle-orm/sqlite-core'export const Users = snakeCase.table('users', {id: text().primaryKey().$defaultFn(() => crypto.randomUUID()),name: text(),email: text().unique(),emailVerified: integer({ mode: 'boolean' }),image: text(),createdAt: integer({ mode: 'timestamp' }).$defaultFn(() => new Date()),updatedAt: integer({ mode: 'timestamp' }).$defaultFn(() => new Date()),})export type SelectUser = typeof Users.$inferSelectexport type InsertUser = typeof Users.$inferInsertexport const Accounts = snakeCase.table('accounts',{userId: text().notNull().references(() => Users.id, { onDelete: 'cascade' }),type: text().notNull(),provider: text().notNull(),providerAccountId: text().notNull(),refreshToken: text(),accessToken: text(),expiresAt: integer(),tokenType: text(),scope: text(),idToken: text(),sessionState: text(),createdAt: integer({ mode: 'timestamp' }).$defaultFn(() => new Date()),},account => [primaryKey({columns: [account.provider, account.providerAccountId],}),],)export type SelectAccount = typeof Accounts.$inferSelectexport type InsertAccount = typeof Accounts.$inferInsertschema.ts import { boolean, integer, primaryKey, snakeCase, text, timestamp } from 'drizzle-orm/pg-core'export const Users = snakeCase.table('users', {id: text().primaryKey(),name: text(),email: text().unique(),emailVerified: boolean(),image: text(),createdAt: timestamp().defaultNow(),updatedAt: timestamp().defaultNow(),})export type SelectUser = typeof Users.$inferSelectexport type InsertUser = typeof Users.$inferInsertexport const Accounts = snakeCase.table('accounts',{userId: text().notNull().references(() => Users.id, { onDelete: 'cascade' }),type: text().notNull(),provider: text().notNull(),providerAccountId: text().notNull(),refreshToken: text(),accessToken: text(),expiresAt: integer(),tokenType: text(),scope: text(),idToken: text(),sessionState: text(),createdAt: timestamp().defaultNow(),},account => [primaryKey({columns: [account.provider, account.providerAccountId],}),],)export type SelectAccount = typeof Accounts.$inferSelectexport type InsertAccount = typeof Accounts.$inferInsert -
Configure DrizzleKit
Section titled “Configure DrizzleKit”Configure the schema path and database credentials for migrations.
drizzle.config.ts import { defineConfig } from 'drizzle-kit'export default defineConfig({schema: './schema.ts',dialect: 'turso',dbCredentials: {authToken: process.env.TURSO_AUTH_TOKEN!,url: process.env.TURSO_DB_URL!,},})drizzle.config.ts import { defineConfig } from 'drizzle-kit'export default defineConfig({schema: './schema.ts',dialect: 'postgresql',dbCredentials: {url: process.env.DATABASE_URL!,},})Now you can run
npx drizzle-kit pushto apply your schema to the database. -
Set up the Database Client
Section titled “Set up the Database Client”Initialize the Drizzle client with your database connection.
db.ts import { createClient } from '@libsql/client'import { drizzle } from 'drizzle-orm/libsql'const client = createClient({url: process.env.TURSO_DB_URL!,authToken: process.env.TURSO_AUTH_TOKEN!,})export const db = drizzle({ client })For Cloudflare D1, create the client from your Worker binding instead:
db.ts import { env } from 'cloudflare:workers'import { drizzle } from 'drizzle-orm/d1'export const db = drizzle(env.DB)db.ts import { drizzle } from 'drizzle-orm/node-postgres'import { Pool } from 'pg'const pool = new Pool({connectionString: process.env.DATABASE_URL!,})export const db = drizzle({ client: pool }) -
Integrate with
Section titled “Integrate with createAuth”createAuthFinally, pass the Drizzle client and schema tables to the
DrizzleAdapterin yourcreateAuthconfiguration.auth.ts import { DrizzleAdapter } from '@rttnd/gau/adapters/drizzle'import { createAuth } from '@rttnd/gau/core'import { GitHub } from '@rttnd/gau/oauth'import { db } from './db'import { Accounts, Users } from './schema'export const auth = createAuth({adapter: DrizzleAdapter(db, Users, Accounts),providers: [GitHub({clientId: process.env.GITHUB_CLIENT_ID!,clientSecret: process.env.GITHUB_CLIENT_SECRET!,}),],jwt: {secret: process.env.AUTH_SECRET!,},})export type Auth = typeof auth
Upgrading from Drizzle 0.x
Section titled “Upgrading from Drizzle 0.x”Upgrade both drizzle-orm and drizzle-kit to the versions above. The adapter call remains DrizzleAdapter(db, Users, Accounts).
- Move
casing: 'snake_case'from the database client and Kit configuration tosnakeCase.table()in the schema. Preserve existing SQL column names; explicit column names still work. - Use
drizzle({ client })for libSQL and PostgreSQL clients. D1 continues to usedrizzle(env.DB). - Remove the old
schemaclient option. Gau does not require relational queries. If your application uses them, migrate to Drizzle’sdefineRelations()API and passrelationsto the client. - Use
timestamp()in PostgreSQL andinteger({ mode: 'timestamp' })in SQLite for the user’screatedAtandupdatedAtfields. Text timestamp columns do not meet the adapter’s schema contract.
Existing Drizzle Kit migration history also needs the Drizzle 1.0 upgrade procedure. Changing the adapter alone does not require new auth tables or renamed columns.