Skip to content

fix(auth): carry Better Auth update hooks and defaults onto generated columns - #764

Merged
cpojer merged 2 commits into
voidzero-dev:mainfrom
karthickeyan-dev:fix/better-auth-column-defaults
Nov 9, 2026
Merged

cpojer merged 2 commits into
voidzero-dev:mainfrom
karthickeyan-dev:fix/better-auth-column-defaults

Conversation

@karthickeyan-dev

@karthickeyan-dev karthickeyan-dev commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Closes #756

Problem

Better Auth applies onUpdate and defaultValue itself on its own writes, but the Drizzle columns in .void/better-auth-schema.ts had neither. Writes that go directly through Drizzle missed them: db.update() on an auth table left updatedAt unchanged, and a row inserted without banned stored null instead of false.

Changes

createColumnDefinition now emits:

  • .$onUpdate(() => new Date()) for Better Auth's built-in updatedAt fields, unless overridden through additional fields or a plugin
  • .$defaultFn(() => <literal>) for string, number and boolean defaults, on optional fields too
"emailVerified": integer("emailVerified", { mode: "boolean" }).notNull().$defaultFn(() => false),
"updatedAt": integer("updatedAt", { mode: "timestamp_ms" }).notNull().$onUpdate(() => new Date()),
"banned": integer("banned", { mode: "boolean" }).$defaultFn(() => false),

Both are runtime-only Drizzle modifiers, so existing apps need no migration. resolveFixedDefault still returns only required fields' defaults, so migrationSchema is unchanged.

Custom update callbacks remain handled by Better Auth and are omitted from the generated Drizzle columns.

Function defaults such as createdAt's () => new Date() are left out, as are date and JSON literals, matching the existing exclusions.

Drizzle also runs $onUpdate on insert when the value is omitted, and treats the column as optional in insert types. For updatedAt this fills in the current time rather than failing the NOT NULL constraint.

Verification

  • New cases in packages/void/test/unit/auth-drizzle.test.ts cover update hooks in all three dialects, required and optional literal defaults, excluded function/date/JSON defaults, and unchanged migration metadata.
  • Ran the generated schemas through real Drizzle and drizzle-kit: snapshots are identical with and without the modifiers on SQLite, PostgreSQL and MySQL; the generated code typechecks in all three; on SQLite a direct update sets updatedAt and an insert gets the literal defaults.
  • vp run check passes.
  • vp test run locally: 16,453 passed, 8 failed.
    • 5 are api-webhooks timeouts that also fail on main in a full run on this machine.
    • 3 are in cf-build-output (timeout) and plugin-cloudflare-v2 (inspector port EADDRINUSE). These did not fail on main, but passed 3 of 3 runs in isolation on this branch and do not reference the auth generator.

🤖 Generated with Claude Code

… columns

Direct Drizzle writes on auth tables skipped `onUpdate` and literal
defaults, which Better Auth only applies on its own writes. Emit them as
runtime-only modifiers so the DDL and existing migrations stay unchanged.

Closes voidzero-dev#756

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@cpojer
cpojer self-requested a review November 9, 2026 22:35
cpojer
cpojer previously approved these changes Nov 9, 2026

@cpojer cpojer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@cpojer
cpojer merged commit dabf761 into voidzero-dev:main Nov 9, 2026
2 checks passed
@karthickeyan-dev
karthickeyan-dev deleted the fix/better-auth-column-defaults branch November 10, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated Better Auth schema drops onUpdate and column defaults

2 participants