Skip to content
Back to skills

Database

ASecurity

Design and implement production database systems. Covers schema design, migrations, query optimization, ORMs (Prisma, Drizzle, TypeORM), PostgreSQL/SQLite/MongoDB patterns, indexing strategies, connection pooling, and multi-tenant architectures. Use when designing schemas, writing queries, or setting up data access layers.

  • 67 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 26, 2026
databasesgosqlnodedatabasesecurity

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add RaheesAhmed/SajiCode --skill database --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Database?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Database
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/raheesahmed-database/badge)](https://www.skillsdirectory.com/skills/raheesahmed-database)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: database-patterns
description: Design and implement production database systems. Covers schema design, migrations, query optimization, ORMs (Prisma, Drizzle, TypeORM), PostgreSQL/SQLite/MongoDB patterns, indexing strategies, connection pooling, and multi-tenant architectures. Use when designing schemas, writing queries, or setting up data access layers.
---

# Database Engineering

## Schema Design Principles

### Naming Conventions
- Table names: singular PascalCase (`User`, `OrderItem`)
- Column names: camelCase (`createdAt`, `userId`)
- Foreign keys: `{referencedTable}Id` (`userId`, `orderId`)
- Indexes: `idx_{table}_{column}` (`idx_user_email`)
- Unique constraints: `uq_{table}_{column}` (`uq_user_email`)

### Required Columns (every table)
```sql
id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
```

### ID Strategy
| Type | Use When |
|------|----------|
| UUID v4 | Public-facing IDs, distributed systems |
| CUID2 | URL-safe, sortable, no collision |
| Auto-increment | Internal-only, sequential ordering needed |
| ULID | Sortable UUID alternative |

## Prisma Patterns

### Schema with Relations
```prisma
model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String
  role      Role     @default(USER)
  posts     Post[]
  profile   Profile?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([email])
  @@index([createdAt])
}

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  author    User     @relation(fields: [authorId], references: [id], onDelete: Cascade)
  authorId  String
  tags      Tag[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([authorId])
  @@index([published, createdAt])
}
```

### Optimized Queries
```ts
// Pagination with cursor (much faster than offset)
const posts = await prisma.post.findMany({
  take: 20,
  skip: 1,
  cursor: { id: lastPostId },
  orderBy: { createdAt: "desc" },
  select: { id: true, title: true, createdAt: true, author: { select: { name: true } } },
});

// Transaction for multi-table writes
const [user, team] = await prisma.$transaction([
  prisma.user.create({ data: userData }),
  prisma.team.update({ where: { id: teamId }, data: { memberCount: { increment: 1 } } }),
]);

// Upsert pattern
await prisma.user.upsert({
  where: { email },
  update: { name, updatedAt: new Date() },
  create: { email, name },
});
```

## Drizzle ORM Patterns

### Schema Definition
```ts
import { pgTable, text, timestamp, boolean, uuid, index } from "drizzle-orm/pg-core";

export const users = pgTable("user", {
  id: uuid("id").defaultRandom().primaryKey(),
  email: text("email").notNull().unique(),
  name: text("name").notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull(),
}, (table) => ({
  emailIdx: index("idx_user_email").on(table.email),
}));
```

## Migration Workflow
```
1. Design schema changes
2. Generate: npx prisma migrate dev --name descriptive_name
3. Review generated SQL — NEVER skip this
4. Test with staging data
5. Deploy: npx prisma migrate deploy
6. NEVER edit committed migrations — create a new one
```

## Query Optimization

### Index Strategy
```sql
-- Composite index for multi-column WHERE clauses (order matters!)
CREATE INDEX idx_post_status_date ON post(published, created_at DESC);

-- Partial index for filtered queries
CREATE INDEX idx_active_users ON "user"(email) WHERE active = true;

-- Covering index (includes all queried columns)
CREATE INDEX idx_post_listing ON post(author_id, created_at DESC) INCLUDE (title);
```

### N+1 Prevention
```ts
// BAD: N+1 queries
const users = await prisma.user.findMany();
for (const user of users) {
  const posts = await prisma.post.findMany({ where: { authorId: user.id } });
}

// GOOD: Eager loading
const users = await prisma.user.findMany({
  include: { posts: { take: 5, orderBy: { createdAt: "desc" } } },
});
```

## Connection Pooling
```ts
// Use connection pooling in production
const prisma = new PrismaClient({
  datasources: { db: { url: process.env.DATABASE_URL } },
  log: process.env.NODE_ENV === "development" ? ["query", "warn", "error"] : ["error"],
});

// Graceful shutdown
process.on("beforeExit", async () => { await prisma.$disconnect(); });
```

## Security Rules
- ALWAYS use parameterized queries — NEVER string concatenation
- NEVER expose internal IDs in URLs — use UUIDs
- Encrypt sensitive fields at rest (passwords, tokens, PII)
- Use row-level security for multi-tenant data
- Audit log all destructive operations (DELETE, UPDATE on sensitive tables)
- Validate all input with Zod BEFORE database operations

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…