From software-development
Database access patterns for TypeScript/Node.js with Prisma, Drizzle, and Knex. Loaded by backend-software-developer-agent when implementing data access layers, writing queries, or designing database schemas.
How this skill is triggered — by the user, by Claude, or both
Slash command
/software-development:database-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Abstract data access behind a typed interface. Keeps business logic ORM-agnostic.
Abstract data access behind a typed interface. Keeps business logic ORM-agnostic.
interface Repository<T, CreateDto, UpdateDto> {
findById(id: string): Promise<T | null>;
findMany(filter?: Partial<T>): Promise<T[]>;
create(data: CreateDto): Promise<T>;
update(id: string, data: UpdateDto): Promise<T>;
delete(id: string): Promise<void>;
}
model User {
id String @id @default(uuid())
email String @unique
name String
orders Order[]
createdAt DateTime @default(now())
}
import { PrismaClient, User, Prisma } from "@prisma/client";
class PrismaUserRepo implements Repository<User, Prisma.UserCreateInput, Prisma.UserUpdateInput> {
constructor(private prisma: PrismaClient) {}
findById(id: string) { return this.prisma.user.findUnique({ where: { id } }); }
findMany() { return this.prisma.user.findMany(); }
create(data: Prisma.UserCreateInput) { return this.prisma.user.create({ data }); }
update(id: string, data: Prisma.UserUpdateInput) {
return this.prisma.user.update({ where: { id }, data });
}
async delete(id: string) { await this.prisma.user.delete({ where: { id } }); }
}
// WRONG: N+1 — fetches orders per user in a loop
const users = await prisma.user.findMany();
for (const u of users) { u.orders = await prisma.order.findMany({ where: { userId: u.id } }); }
// CORRECT: Eager load with include
const users = await prisma.user.findMany({ include: { orders: true } });
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: uuid("id").primaryKey().defaultRandom(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
createdAt: timestamp("created_at").defaultNow(),
});
// Typed query
const result = await db.select().from(users).where(eq(users.email, "[email protected]"));
// result is typeof { id: string; email: string; name: string; createdAt: Date }[]
const usersWithOrders = await db
.select()
.from(users)
.leftJoin(orders, eq(users.id, orders.userId));
interface UserRow { id: string; email: string; name: string; created_at: Date; }
const users = await knex<UserRow>("users")
.where("email", "like", "%@example.com")
.orderBy("created_at", "desc")
.limit(20);
down — reversible migrations save production incidents.20240315_add_users_email_index.ts.// Prisma — set pool in connection string
// postgresql://user:pass@host:5432/db?connection_limit=20&pool_timeout=10
// Knex
const knex = require("knex")({
client: "pg",
connection: { /* ... */ },
pool: { min: 2, max: 20, idleTimeoutMillis: 30000 },
});
Rules of thumb:
(core_count * 2) + spindle_count (for traditional setups)pool_timeout / idleTimeoutMillis to avoid leaked connections// Prisma interactive transaction
await prisma.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
await tx.order.create({ data: { ...orderData, userId: user.id } });
});
// Knex transaction
await knex.transaction(async (trx) => {
const [user] = await trx("users").insert(userData).returning("*");
await trx("orders").insert({ ...orderData, user_id: user.id });
});
EXPLAIN ANALYZE to verify query plans before shippingselect over select * — fetch only needed columnsdeleted_at timestamp) for audit-critical tablesnpx claudepluginhub bartekck/bartek-marketplace --plugin software-developmentProduction patterns and non-obvious traps for Prisma ORM in TypeScript backends — schema design, query optimization, transactions, pagination, bulk operations, migrations, and serverless deployment gotchas.
Provides ORM best practices for Drizzle and Prisma: N+1 prevention, migrations, transactions, batch operations, and query optimization. Useful for reviewing database queries and schemas.
Provides Drizzle ORM patterns for schema definition, CRUD operations, relations, queries, transactions, and migrations. Supports PostgreSQL, MySQL, SQLite, MSSQL, CockroachDB.