Learn how to extend the database schema with custom tables and relationships
The Auth UI Boilerplate comes with Better Auth tables, but you’ll likely need to add custom tables for your application. This guide shows you how to extend the schema with your own tables.
// Set to null when parent is deletedreferences(() => user.id, { onDelete: "set null" })// Prevent deletion if children existreferences(() => user.id, { onDelete: "restrict" })// Set to default value when parent is deletedreferences(() => user.id, { onDelete: "set default" })// Do nothing (database default)references(() => user.id, { onDelete: "no action" })
import { db } from "@/db";import { post } from "@/db/schema";import { eq, desc } from "drizzle-orm";// Select all postsconst allPosts = await db.select().from(post);// Select with conditionconst userPosts = await db .select() .from(post) .where(eq(post.userId, "user_123"));// Select with orderingconst recentPosts = await db .select() .from(post) .orderBy(desc(post.createdAt));// Select specific columnsconst titles = await db .select({ title: post.title }) .from(post);
import { db } from "@/db";import { post, user } from "@/db/schema";import { eq } from "drizzle-orm";const postsWithAuthors = await db .select({ postId: post.id, title: post.title, authorName: user.name, authorEmail: user.email, }) .from(post) .leftJoin(user, eq(post.userId, user.id));
import { db } from "@/db";import { post } from "@/db/schema";import { nanoid } from "nanoid";const newPost = await db.insert(post).values({ id: nanoid(), title: "My First Post", userId: "user_123", createdAt: new Date(),});
import { db } from "@/db";import { post } from "@/db/schema";import { eq } from "drizzle-orm";await db .update(post) .set({ title: "Updated Title" }) .where(eq(post.id, "post_123"));
Drizzle automatically infers TypeScript types from your schema:
import { post } from "@/db/schema";import type { InferSelectModel, InferInsertModel } from "drizzle-orm";// Type for selecting from databasetype Post = InferSelectModel<typeof post>;// { id: string; title: string; userId: string; createdAt: Date }// Type for inserting into databasetype NewPost = InferInsertModel<typeof post>;// { id: string; title: string; userId: string; createdAt?: Date }
Use these types in your functions:
import type { InferSelectModel } from "drizzle-orm";import { post } from "@/db/schema";type Post = InferSelectModel<typeof post>;function displayPost(post: Post) { console.log(post.title); console.log(post.createdAt);}
Always test schema changes with db:push locally before generating migrations:
# 1. Add table to schema.ts# 2. Test locallynpm run db:pushnpm run db:studio # Verify in Studio# 3. Generate migration for productionnpm run db:generate