From 5ebdef19c1be366ffa8ed1a873828ca939b277af Mon Sep 17 00:00:00 2001 From: Ahmed Abdellahi Abdat Date: Wed, 15 Oct 2025 14:45:26 +0200 Subject: [PATCH] docs: clarify generateId behavior and mixed ID types (#5173) Co-authored-by: Maxwell <145994855+ping-maxwell@users.noreply.github.com> --- docs/content/docs/concepts/database.mdx | 90 +++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 5 deletions(-) diff --git a/docs/content/docs/concepts/database.mdx b/docs/content/docs/concepts/database.mdx index 25f111cd6e..91791110d5 100644 --- a/docs/content/docs/concepts/database.mdx +++ b/docs/content/docs/concepts/database.mdx @@ -471,11 +471,11 @@ export const auth = betterAuth({ ### ID Generation -Better Auth by default will generate unique IDs for users, sessions, and other entities. If you want to customize how IDs are generated, you can configure this in the `advanced.database.generateId` option in your auth config. +Better Auth by default will generate unique IDs for users, sessions, and other entities. You can customize ID generation behavior using the `advanced.database.generateId` option or `advanced.database.useNumberId`. -You can also disable ID generation by setting the `advanced.database.generateId` option to `false`. This will assume your database will generate the ID automatically. +#### Option 1: Let Database Generate IDs -**Example: Automatic Database IDs** +Setting `generateId` to `false` disables ID generation globally, letting your database handle all ID generation: ```ts title="auth.ts" import { betterAuth } from "better-auth"; @@ -485,13 +485,48 @@ export const auth = betterAuth({ database: db, advanced: { database: { - generateId: false, + generateId: false, // Database generates all IDs }, }, }); ``` -**Example: Using a Custom ID Generator** + + **Note**: For auto-incrementing numeric IDs (serial, auto_increment), use `useNumberId: true` instead (see the Numeric IDs section below). + + +#### Option 2: Custom ID Generation Function + +Use a function to generate IDs. You can return `false` or `undefined` from the function to let the database generate the ID for specific models: + +```ts title="auth.ts" +import { betterAuth } from "better-auth"; +import { db } from "./db"; + +export const auth = betterAuth({ + database: db, + advanced: { + database: { + generateId: (options) => { + // Let database auto-generate for specific models + if (options.model === "user" || options.model === "users") { + return false; // Let database generate ID + } + // Generate UUIDs for other tables + return crypto.randomUUID(); + }, + }, + }, +}); +``` + + + **Important**: Returning `false` or `undefined` from the `generateId` function lets the database handle ID generation for that specific model. Setting `generateId: false` (without a function) disables ID generation for **all** tables. + + +#### Option 3: Consistent Custom ID Generator + +Generate the same type of ID for all tables: ```ts title="auth.ts" import { betterAuth } from "better-auth"; @@ -538,6 +573,50 @@ export const auth = betterAuth({ this is normal. It's also expected that all id values passed to Better-Auth (eg via an endpoint body) is expected to be a string. +### Mixed ID Types + +If you need different ID types across tables (e.g., integer IDs for users, UUID strings for sessions/accounts/verification), use a `generateId` function instead of `useNumberId`. + + + **Do NOT use `useNumberId` when mixing ID types**. The `useNumberId` option is global and affects all tables. It will disable internal ID generation and expect all IDs to be numbers. + + +**Example: Integer IDs for Users, UUIDs for Other Tables** + +```ts title="auth.ts" +import { betterAuth } from "better-auth"; +import { db } from "./db"; + +export const auth = betterAuth({ + database: db, + user: { + modelName: "users", // PostgreSQL: id serial primary key + }, + session: { + modelName: "session", // PostgreSQL: id text primary key + }, + advanced: { + database: { + // Do NOT set useNumberId - it's global and affects all tables + generateId: (options) => { + if (options.model === "user" || options.model === "users") { + return false; // Let PostgreSQL serial generate it + } + return crypto.randomUUID(); // UUIDs for session, account, verification + }, + }, + }, +}); +``` + +This configuration allows you to: +- Use database auto-increment (serial, auto_increment, etc.) for the users table +- Generate UUIDs for all other tables (session, account, verification) +- Maintain compatibility with existing schemas that use different ID types + + + **Use Case**: This is particularly useful when migrating from other authentication providers (like Clerk) where you have existing users with integer IDs but want UUID strings for new tables. + ### Database Hooks @@ -616,6 +695,7 @@ export const auth = betterAuth({ }); ``` + #### Throwing Errors If you want to stop the database hook from proceeding, you can throw errors using the `APIError` class imported from `better-auth/api`.