feat: add auth template schema files and update core config#12
feat: add auth template schema files and update core config#12weroperking merged 2 commits intomainfrom
Conversation
📝 WalkthroughWalkthroughAdds Turso-specific cross-field validation to core config schema, a small type-safety tweak in dashboard logs focus handling, a comment TODO in a provider interface, and new authentication database wiring and SQLite schema helpers (Bun + Drizzle) in the auth template. Changes
Sequence Diagram(s)sequenceDiagram
participant Env as Env (DB_PATH)
participant AuthInit as Auth DB init
participant BunDB as Bun SQLite DB
participant Drizzle as Drizzle ORM
participant Schema as Auth Schema (users/sessions/accounts)
Env->>AuthInit: read DB_PATH (or default ./data/auth.db)
AuthInit->>BunDB: create/open SQLite DB (create: true)
BunDB->>Drizzle: provide DB connection
AuthInit->>Drizzle: create Drizzle instance with merged schema
Drizzle->>Schema: bind tables (users, sessions, accounts)
AuthInit->>Consumers: export `db`, `users`, `sessions`, `accounts`
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
betterbase/packages/core/src/config/schema.ts (1)
16-17: LGTM — consider optional Zod cross-validation for provider-specific required fields.All three connection fields (
connectionString,url,authToken) are fully optional with no mutual dependency enforced at the schema level. Atursoprovider needsurl+authToken; other providers needconnectionString. Without a refinement, a misconfiguredBetterBaseConfigwon't be caught until runtime inside the provider adapter.♻️ Example: enforce connection fields per provider type via
.superRefine()provider: z.object({ type: ProviderTypeSchema, connectionString: z.string().optional(), url: z.string().optional(), // Turso - libSQL connection URL authToken: z.string().optional(), // Turso - auth token for managed DB - }), + }).superRefine((data, ctx) => { + if (data.type === 'turso') { + if (!data.url) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'url is required for turso', path: ['url'] }); + if (!data.authToken) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'authToken is required for turso', path: ['authToken'] }); + } else if (data.type !== 'managed') { + if (!data.connectionString) ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'connectionString is required for this provider', path: ['connectionString'] }); + } + }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@betterbase/packages/core/src/config/schema.ts` around lines 16 - 17, Add a Zod .superRefine() to the existing config schema to enforce provider-specific required fields: when provider === 'turso' require both url and authToken to be present and non-empty; for other providers require connectionString; emit clear refinement errors pointing to the missing fields (e.g., add issues for "url" and "authToken" or "connectionString"). Update the schema that declares connectionString, url, and authToken to include this .superRefine() logic so misconfigured BetterBaseConfig values are caught at validation time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@betterbase/templates/auth/src/db/index.ts`:
- Around line 3-4: The Drizzle schema passed to drizzle(sqlite, { schema }) only
includes the ./schema namespace and omits sessions and accounts from
./auth-schema, causing db.query.sessions and db.query.accounts to be undefined;
fix by importing the users table explicitly (e.g., import { users } from
'./schema') and import the auth tables (e.g., import * as authSchema from
'./auth-schema' or import { sessions, accounts } from './auth-schema'), then
pass a merged schema object to drizzle such as drizzle(sqlite, { schema: { ...{
users }, ...authSchema } }) so that sessions and accounts are registered with
Drizzle and relational queries work.
In `@betterbase/templates/auth/src/db/schema.ts`:
- Around line 24-29: The timestamps definition uses client-side hooks
($defaultFn/$onUpdate) but lacks NOT NULL constraints, so update the timestamps
object (createdAt and updatedAt properties defined via text('created_at') and
text('updated_at')) to include .notNull() on both columns while preserving their
existing $defaultFn and $onUpdate calls so the generated DDL enforces NOT NULL
at the database level.
- Around line 44-50: The users table definition (users) is missing the
BetterAuth-required emailVerified field and uses a plural table name; add an
emailVerified column to the sqliteTable('users') definition (matching
BetterAuth's expected type/nullable behavior) and then update the BetterAuth
adapter wiring (drizzleAdapter / betterAuth config) to map the plural users
table to the expected user schema name and register status/deletedAt as
additionalFields in the user config so the adapter sees the correct schema
mapping and extra fields.
---
Nitpick comments:
In `@betterbase/packages/core/src/config/schema.ts`:
- Around line 16-17: Add a Zod .superRefine() to the existing config schema to
enforce provider-specific required fields: when provider === 'turso' require
both url and authToken to be present and non-empty; for other providers require
connectionString; emit clear refinement errors pointing to the missing fields
(e.g., add issues for "url" and "authToken" or "connectionString"). Update the
schema that declares connectionString, url, and authToken to include this
.superRefine() logic so misconfigured BetterBaseConfig values are caught at
validation time.
| export const users = sqliteTable('users', { | ||
| id: uuid(), | ||
| email: text('email').notNull().unique(), | ||
| name: text('name'), | ||
| status: statusEnum(), | ||
| ...timestamps, | ||
| ...softDelete, |
There was a problem hiding this comment.
users table is missing BetterAuth-required emailVerified field and uses plural table name.
BetterAuth's core schema has four essential tables — user, session, account, verification — each with fixed required fields. The user table requires an emailVerified column. Without it, the BetterAuth Drizzle adapter will either error or silently skip email verification state, breaking auth flows.
Additionally, the Drizzle adapter expects the schema you define to match BetterAuth's expected table names; if your Drizzle schema maps the user table to users, you need to manually pass the schema and map it to the user table in the adapter config.
🔧 Proposed fix — add emailVerified and register as BetterAuth additionalFields
export const users = sqliteTable('users', {
id: uuid(),
email: text('email').notNull().unique(),
+ emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false),
name: text('name'),
status: statusEnum(),
...timestamps,
...softDelete,
});And in the BetterAuth config wire up the adapter with the plural-name mapping and declare status/deletedAt as additionalFields:
// auth.ts
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { db, users, sessions, accounts } from './db';
export const auth = betterAuth({
database: drizzleAdapter(db, {
provider: 'sqlite',
schema: { user: users, session: sessions, account: accounts },
}),
user: {
additionalFields: {
status: { type: 'string', defaultValue: 'active' },
deletedAt: { type: 'string', required: false },
},
},
});Based on learnings: "Applies to **/auth/**/*.{ts,tsx}: Use BetterAuth for authentication implementation direction."
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const users = sqliteTable('users', { | |
| id: uuid(), | |
| email: text('email').notNull().unique(), | |
| name: text('name'), | |
| status: statusEnum(), | |
| ...timestamps, | |
| ...softDelete, | |
| export const users = sqliteTable('users', { | |
| id: uuid(), | |
| email: text('email').notNull().unique(), | |
| emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false), | |
| name: text('name'), | |
| status: statusEnum(), | |
| ...timestamps, | |
| ...softDelete, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@betterbase/templates/auth/src/db/schema.ts` around lines 44 - 50, The users
table definition (users) is missing the BetterAuth-required emailVerified field
and uses a plural table name; add an emailVerified column to the
sqliteTable('users') definition (matching BetterAuth's expected type/nullable
behavior) and then update the BetterAuth adapter wiring (drizzleAdapter /
betterAuth config) to map the plural users table to the expected user schema
name and register status/deletedAt as additionalFields in the user config so the
adapter sees the correct schema mapping and extra fields.
- Add Turso-specific validation in BetterBaseConfigSchema - Improve auth template db schema with proper user table - Merge all schemas for Drizzle in auth template
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
betterbase/templates/auth/src/db/index.ts (1)
3-3:import *spreads non-table helpers into the Drizzle schema.
./schemaexports utility helpers (uuid,timestamps,statusEnum,softDelete) alongside theuserstable. Spreading them all intofullSchemaworks because Drizzle silently ignores non-table entries, but it muddies the schema object. A named import keeps intent clear:♻️ Suggested change
-import * as schema from './schema'; -import { sessions, accounts } from './auth-schema'; +import { users } from './schema'; +import * as authSchema from './auth-schema'; // Merge all schemas for Drizzle const fullSchema = { - ...schema, - sessions, - accounts, + users, + ...authSchema, };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@betterbase/templates/auth/src/db/index.ts` at line 3, The import currently uses a namespace import "schema" from './schema' and then spreads it into fullSchema, which mixes non-table helpers (uuid, timestamps, statusEnum, softDelete) with table definitions (users); change the import to a named import that only brings in the table exports you intend to include in fullSchema (e.g., import { users } from './schema') and keep utility helpers (uuid, timestamps, statusEnum, softDelete) as separate named imports if needed elsewhere so fullSchema only contains table definitions and not helper utilities.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@betterbase/templates/auth/src/db/index.ts`:
- Around line 15-17: The code constructs DB_PATH and immediately instantiates
Database (sqlite) which will fail if parent directories don't exist; before
calling new Database(DB_PATH, ...) ensure the parent directory of DB_PATH exists
(use path.dirname(DB_PATH)) and create it with fs.mkdirSync or await
fs.promises.mkdir(dir, { recursive: true }) and handle errors, then instantiate
sqlite = new Database(DB_PATH, { create: true }); update the module where
DB_PATH, sqlite and Database are defined so the directory creation runs
synchronously or before sqlite is assigned.
---
Nitpick comments:
In `@betterbase/templates/auth/src/db/index.ts`:
- Line 3: The import currently uses a namespace import "schema" from './schema'
and then spreads it into fullSchema, which mixes non-table helpers (uuid,
timestamps, statusEnum, softDelete) with table definitions (users); change the
import to a named import that only brings in the table exports you intend to
include in fullSchema (e.g., import { users } from './schema') and keep utility
helpers (uuid, timestamps, statusEnum, softDelete) as separate named imports if
needed elsewhere so fullSchema only contains table definitions and not helper
utilities.
| const DB_PATH = process.env.DB_PATH || './data/auth.db'; | ||
|
|
||
| const sqlite = new Database(DB_PATH, { create: true }); |
There was a problem hiding this comment.
Parent directory may not exist on first run.
new Database(DB_PATH, { create: true }) creates the .db file but won't create intermediate directories. If ./data/ doesn't exist, this will throw an ENOENT error. For a template that newcomers will clone and run, consider ensuring the directory exists:
💡 Suggested safeguard
+import { mkdirSync } from 'node:fs';
+import { dirname } from 'node:path';
+
const DB_PATH = process.env.DB_PATH || './data/auth.db';
+mkdirSync(dirname(DB_PATH), { recursive: true });
const sqlite = new Database(DB_PATH, { create: true });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const DB_PATH = process.env.DB_PATH || './data/auth.db'; | |
| const sqlite = new Database(DB_PATH, { create: true }); | |
| import { mkdirSync } from 'node:fs'; | |
| import { dirname } from 'node:path'; | |
| const DB_PATH = process.env.DB_PATH || './data/auth.db'; | |
| mkdirSync(dirname(DB_PATH), { recursive: true }); | |
| const sqlite = new Database(DB_PATH, { create: true }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@betterbase/templates/auth/src/db/index.ts` around lines 15 - 17, The code
constructs DB_PATH and immediately instantiates Database (sqlite) which will
fail if parent directories don't exist; before calling new Database(DB_PATH,
...) ensure the parent directory of DB_PATH exists (use path.dirname(DB_PATH))
and create it with fs.mkdirSync or await fs.promises.mkdir(dir, { recursive:
true }) and handle errors, then instantiate sqlite = new Database(DB_PATH, {
create: true }); update the module where DB_PATH, sqlite and Database are
defined so the directory creation runs synchronously or before sqlite is
assigned.
This PR adds auth template schema files and updates core config.
Summary by CodeRabbit
New Features
New Features / Validation
Chores