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.
npx claudepluginhub bartekck/bartek-marketplace --plugin software-developmentThis skill uses the workspace's default tool permissions.
Abstract data access behind a typed interface. Keeps business logic ORM-agnostic.
Defines type-safe database schemas, queries, relations, and migrations using Drizzle ORM in TypeScript for PostgreSQL, MySQL, SQLite, Cloudflare D1, and Durable Objects.
Provides Drizzle ORM patterns for schema definition, CRUD operations, relations, queries, transactions, and migrations. Supports PostgreSQL, MySQL, SQLite, MSSQL, CockroachDB.
Provides expert guidance on Prisma ORM for TypeScript apps: schema design, migrations, Prisma Client queries, relations, edge deployment, and performance optimization.
Share bugs, ideas, or general feedback.
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, "a@b.com"));
// 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 tables