diff --git a/packages/cli/src/generators/drizzle.ts b/packages/cli/src/generators/drizzle.ts index 9fb1fbc572..6d08d53db1 100644 --- a/packages/cli/src/generators/drizzle.ts +++ b/packages/cli/src/generators/drizzle.ts @@ -3,6 +3,7 @@ import { type BetterAuthDbSchema, type FieldAttribute, } from "better-auth/db"; +import type { BetterAuthOptions } from "better-auth/types"; import { existsSync } from "fs"; import type { SchemaGenerator } from "./types"; @@ -27,7 +28,7 @@ export const generateDrizzleSchema: SchemaGenerator = async ({ } const fileExist = existsSync(filePath); - let code: string = generateImport({ databaseType, tables }); + let code: string = generateImport({ databaseType, tables, options }); for (const tableKey in tables) { const table = tables[tableKey]!; @@ -117,7 +118,11 @@ export const generateDrizzleSchema: SchemaGenerator = async ({ let id: string = ""; if (options.advanced?.database?.useNumberId) { - id = `int("id").autoincrement.primaryKey()`; + if (databaseType === "pg") { + id = `serial("id").primaryKey()`; + } else { + id = `int("id").autoincrement.primaryKey()`; + } } else { if (databaseType === "mysql") { id = `varchar('id', { length: 36 }).primaryKey()`; @@ -173,13 +178,20 @@ export const generateDrizzleSchema: SchemaGenerator = async ({ function generateImport({ databaseType, tables, -}: { databaseType: "sqlite" | "mysql" | "pg"; tables: BetterAuthDbSchema }) { + options, +}: { + databaseType: "sqlite" | "mysql" | "pg"; + tables: BetterAuthDbSchema; + options: BetterAuthOptions; +}) { let imports: string[] = []; const hasBigint = Object.values(tables).some((table) => Object.values(table.fields).some((field) => field.bigint), ); + const useNumberId = options.advanced?.database?.useNumberId; + imports.push(`${databaseType}Table`); imports.push( databaseType === "mysql" @@ -191,6 +203,7 @@ function generateImport({ imports.push(hasBigint ? (databaseType !== "sqlite" ? "bigint" : "") : ""); imports.push(databaseType !== "sqlite" ? "timestamp, boolean" : ""); imports.push(databaseType === "mysql" ? "int" : "integer"); + imports.push(useNumberId ? (databaseType === "pg" ? "serial" : "") : ""); return `import { ${imports .map((x) => x.trim()) diff --git a/packages/cli/test/__snapshots__/auth-schema-number-id.txt b/packages/cli/test/__snapshots__/auth-schema-number-id.txt new file mode 100644 index 0000000000..23977bd252 --- /dev/null +++ b/packages/cli/test/__snapshots__/auth-schema-number-id.txt @@ -0,0 +1,57 @@ +import { pgTable, text, timestamp, boolean, integer, serial } from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: serial("id").primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + emailVerified: boolean('email_verified').$defaultFn(() => false).notNull(), + image: text('image'), + createdAt: timestamp('created_at').$defaultFn(() => /* @__PURE__ */ new Date()).notNull(), + updatedAt: timestamp('updated_at').$defaultFn(() => /* @__PURE__ */ new Date()).notNull(), + twoFactorEnabled: boolean('two_factor_enabled'), + username: text('username').unique(), + displayUsername: text('display_username') + }); + +export const session = pgTable("session", { + id: serial("id").primaryKey(), + expiresAt: timestamp('expires_at').notNull(), + token: text('token').notNull().unique(), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: serial('user_id').primaryKey().notNull().references(()=> user.id, { onDelete: 'cascade' }) + }); + +export const account = pgTable("account", { + id: serial("id").primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: serial('user_id').primaryKey().notNull().references(()=> user.id, { onDelete: 'cascade' }), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: timestamp('access_token_expires_at'), + refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), + scope: text('scope'), + password: text('password'), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull() + }); + +export const verification = pgTable("verification", { + id: serial("id").primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: timestamp('expires_at').notNull(), + createdAt: timestamp('created_at').$defaultFn(() => /* @__PURE__ */ new Date()), + updatedAt: timestamp('updated_at').$defaultFn(() => /* @__PURE__ */ new Date()) + }); + +export const twoFactor = pgTable("two_factor", { + id: serial("id").primaryKey(), + secret: text('secret').notNull(), + backupCodes: text('backup_codes').notNull(), + userId: serial('user_id').primaryKey().notNull().references(()=> user.id, { onDelete: 'cascade' }) + }); diff --git a/packages/cli/test/generate.test.ts b/packages/cli/test/generate.test.ts index c4110eb549..edeba4de0b 100644 --- a/packages/cli/test/generate.test.ts +++ b/packages/cli/test/generate.test.ts @@ -132,6 +132,37 @@ describe("generate", async () => { expect(schema.code).toMatchFileSnapshot("./__snapshots__/auth-schema.txt"); }); + it("should generate drizzle schema with number id", async () => { + const schema = await generateDrizzleSchema({ + file: "test.drizzle", + adapter: drizzleAdapter( + {}, + { + provider: "pg", + schema: {}, + }, + )({} as BetterAuthOptions), + options: { + database: drizzleAdapter( + {}, + { + provider: "pg", + schema: {}, + }, + ), + plugins: [twoFactor(), username()], + advanced: { + database: { + useNumberId: true, + }, + }, + }, + }); + expect(schema.code).toMatchFileSnapshot( + "./__snapshots__/auth-schema-number-id.txt", + ); + }); + it("should generate kysely schema", async () => { const schema = await generateMigrations({ file: "test.sql",