with one click
database
Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation
Menu
Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation
| name | database |
| description | Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation |
For advanced patterns, migrations, and query examples, see reference.md.
packages/database/
├── src/
│ ├── client.ts # Drizzle client
│ ├── schema/ # Drizzle schemas
│ │ ├── users.ts
│ │ ├── sessions.ts
│ │ ├── accounts.ts
│ │ ├── verifications.ts
│ │ └── index.ts
│ └── index.ts # Public exports
├── drizzle.config.ts # Drizzle config
└── package.json
// packages/database/src/client.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import * as schema from './schema';
export const db = drizzle(process.env.DATABASE_URL!, {
casing: 'snake_case', // Auto-converts camelCase ↔ snake_case
schema,
});
// packages/database/src/schema/users.ts
import { pgTable, text, timestamp, boolean } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: text().primaryKey(),
name: text().notNull(),
email: text().notNull().unique(),
emailVerified: boolean().notNull().default(false),
image: text(),
createdAt: timestamp({ mode: 'date' }).notNull().defaultNow(),
updatedAt: timestamp({ mode: 'date' })
.notNull()
.defaultNow()
.$onUpdate(() => new Date()),
});
// Type inference
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
import { relations } from 'drizzle-orm';
export const usersRelations = relations(users, ({ many }) => ({
sessions: many(sessions),
accounts: many(accounts),
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, {
fields: [posts.authorId],
references: [users.id],
}),
}));
// Find one with relations
const user = await db.query.users.findFirst({
where: eq(users.id, userId),
with: { sessions: true, posts: true },
});
// Find many with filters
const activePosts = await db.query.posts.findMany({
where: eq(posts.published, true),
orderBy: desc(posts.createdAt),
limit: 10,
});
// Select
const allUsers = await db.select().from(users);
const names = await db.select({ name: users.name }).from(users);
// With conditions
const admins = await db.select().from(users).where(eq(users.role, 'admin'));
// Insert
const [newUser] = await db
.insert(users)
.values({ id: crypto.randomUUID(), name: 'John', email: 'john@example.com' })
.returning();
// Update
const [updated] = await db
.update(users)
.set({ name: 'Jane' })
.where(eq(users.id, userId))
.returning();
// Delete
await db.delete(users).where(eq(users.id, userId));
await db.transaction(async (tx) => {
const [user] = await tx.insert(users).values({ ... }).returning();
await tx.insert(accounts).values({ userId: user.id, ... });
});
import {
eq, // Equal
ne, // Not equal
gt, // Greater than
gte, // Greater than or equal
lt, // Less than
lte, // Less than or equal
like, // LIKE pattern
ilike, // Case-insensitive LIKE
inArray, // IN (array)
isNull, // IS NULL
and, // AND
or, // OR
} from 'drizzle-orm';
// Examples
const results = await db
.select()
.from(posts)
.where(
and(
eq(posts.authorId, userId),
or(eq(posts.status, 'published'), eq(posts.status, 'draft')),
),
);
const page = 1;
const pageSize = 10;
const posts = await db.query.posts.findMany({
orderBy: desc(posts.createdAt),
limit: pageSize,
offset: (page - 1) * pageSize,
});
| Mistake | Correct Pattern |
|---|---|
Missing .returning() on mutate | Always use .returning() to get results |
Using any for query results | Use $inferSelect / $inferInsert types |
| Not using transactions | Wrap related mutations in transaction |
Forgetting casing: 'snake_case' | Set in drizzle config for auto-conversion |
| Hardcoding IDs | Use crypto.randomUUID() for UUIDs |
| Missing indexes on foreign keys | Add indexes for frequently queried FKs |
Not handling null from findFirst | Check for undefined before using result |
| Using raw SQL for simple queries | Prefer query builder for type safety |
code-reviewer agentGit workflow for branches, commits, and PRs. Use for commit, branch, pr, pull request, conventional, push, feat, fix, chore, merge, rebase
Storybook stories and interaction tests. Use for story, stories, storybook, chromatic, visual test, interaction test, play function, component test
TypeScript and unicorn linting patterns. Use for typescript, array, for-of, reduce, forEach, throw, catch, modern js, es modules, string, number, error handling
Zod v4 schema validation. Use for zod, schema, validation, parse, safeParse, infer, coerce, transform, refine, z.object, z.string, z.email, z.url
Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
React error boundaries + fallback UIs. Use for error, catch, boundary, fallback, recovery, crash, ErrorBoundary, errorComponent, reset