skills/db-postgres/SKILL.md
PostgreSQL database management with Drizzle ORM, versioned migrations, and type-safe queries. This skill should be used when setting up a new database, writing migrations, managing schemas, or troubleshooting database issues in PostgreSQL projects.
npx skillsauth add aussiegingersnap/cursor-skills db-postgresInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive patterns for PostgreSQL database management in Node.js/TypeScript projects using Drizzle ORM, including a versioned migration system and local Docker development.
PostgreSQL is appropriate when:
Singular table names are enforced:
user not userssession not sessionsaccount not accountsThis convention improves readability in code where you reference user.id rather than users.id.
npm install drizzle-orm postgres
npm install -D drizzle-kit @types/node
src/lib/db/
├── index.ts # Connection, migrations, types
├── schema.ts # Drizzle schema definitions
├── migrate.ts # Migration runner
└── queries.ts # Typed query functions (optional)
drizzle/
├── migrations/ # Generated SQL migrations
└── meta/ # Migration metadata
# .env.local (development)
DATABASE_URL=postgres://postgres:postgres@localhost:5432/myapp
# Production
DATABASE_URL=postgres://user:password@host:5432/myapp?sslmode=require
Create src/lib/db/index.ts:
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
throw new Error('DATABASE_URL environment variable is required');
}
// Connection for queries
const queryClient = postgres(connectionString);
// Connection for migrations (with max 1 connection)
const migrationClient = postgres(connectionString, { max: 1 });
export const db = drizzle(queryClient, { schema });
export const migrationDb = drizzle(migrationClient);
export * from './schema';
Create drizzle.config.ts at project root:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './src/lib/db/schema.ts',
out: './drizzle/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.DATABASE_URL!,
},
verbose: true,
strict: true,
});
Create src/lib/db/schema.ts:
import { pgTable, text, timestamp, integer, boolean, uuid, varchar, index, uniqueIndex } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// Example: Define tables here as needed
// Use singular table names: user, session, account
// Type exports - infer from schema
// export type User = typeof user.$inferSelect;
// export type NewUser = typeof user.$inferInsert;
Primary Keys - Use TEXT UUIDs or SERIAL integers:
// UUID primary key (recommended for user-facing entities)
export const user = pgTable('user', {
id: uuid('id').primaryKey().defaultRandom(),
// ...
});
// Serial primary key (for internal/junction tables)
export const auditLog = pgTable('audit_log', {
id: integer('id').primaryKey().generatedAlwaysAsIdentity(),
// ...
});
Timestamps - Always include created/updated:
export const user = pgTable('user', {
id: uuid('id').primaryKey().defaultRandom(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
Foreign Keys - Use cascading deletes for dependent data:
export const session = pgTable('session', {
id: text('id').primaryKey(),
userId: uuid('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }),
// ...
});
Indexes - Create for frequently queried columns:
export const user = pgTable('user', {
id: uuid('id').primaryKey().defaultRandom(),
email: varchar('email', { length: 255 }).notNull(),
}, (table) => ({
emailIdx: uniqueIndex('user_email_idx').on(table.email),
}));
Enums - Use PostgreSQL enums for fixed values:
import { pgEnum } from 'drizzle-orm/pg-core';
export const userRoleEnum = pgEnum('user_role', ['admin', 'member', 'guest']);
export const user = pgTable('user', {
id: uuid('id').primaryKey().defaultRandom(),
role: userRoleEnum('role').notNull().default('member'),
});
Define relations for type-safe joins:
export const userRelations = relations(user, ({ many }) => ({
sessions: many(session),
accounts: many(account),
}));
export const sessionRelations = relations(session, ({ one }) => ({
user: one(user, {
fields: [session.userId],
references: [user.id],
}),
}));
# Generate migration from schema changes
npx drizzle-kit generate
# Generate with custom name
npx drizzle-kit generate --name add_user_table
Create src/lib/db/migrate.ts:
import { migrate } from 'drizzle-orm/postgres-js/migrator';
import { migrationDb } from './index';
async function runMigrations() {
console.log('Running migrations...');
await migrate(migrationDb, {
migrationsFolder: './drizzle/migrations',
});
console.log('Migrations complete');
process.exit(0);
}
runMigrations().catch((err) => {
console.error('Migration failed:', err);
process.exit(1);
});
Add to package.json:
{
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "tsx src/lib/db/migrate.ts",
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio"
}
}
src/lib/db/schema.tsnpm run db:generatedrizzle/migrations/npm run db:migrateimport { db, user } from '@/lib/db';
import { eq, and, or, desc, asc } from 'drizzle-orm';
// Insert
const newUser = await db.insert(user).values({
email: '[email protected]',
name: 'John Doe',
}).returning();
// Select one
const foundUser = await db.query.user.findFirst({
where: eq(user.email, '[email protected]'),
});
// Select many with conditions
const users = await db.query.user.findMany({
where: and(
eq(user.role, 'member'),
eq(user.active, true)
),
orderBy: desc(user.createdAt),
limit: 10,
});
// Update
await db.update(user)
.set({ name: 'Jane Doe', updatedAt: new Date() })
.where(eq(user.id, userId));
// Delete
await db.delete(user).where(eq(user.id, userId));
// Fetch user with sessions
const userWithSessions = await db.query.user.findFirst({
where: eq(user.id, userId),
with: {
sessions: true,
},
});
// Nested relations
const userFull = await db.query.user.findFirst({
where: eq(user.id, userId),
with: {
sessions: true,
accounts: {
columns: {
provider: true,
providerAccountId: true,
},
},
},
});
await db.transaction(async (tx) => {
const [newUser] = await tx.insert(user).values({
email: '[email protected]',
}).returning();
await tx.insert(session).values({
id: generateSessionId(),
userId: newUser.id,
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
});
});
See docker-local skill for complete Docker Compose setup.
Quick start:
# Start PostgreSQL
docker compose up -d postgres
# Run migrations
npm run db:migrate
# Open Drizzle Studio
npm run db:studio
Ensure PostgreSQL is running:
docker compose ps
docker compose logs postgres
Migrations haven't run:
npm run db:migrate
Regenerate types:
npx drizzle-kit generate
For serverless environments, use connection pooling:
const queryClient = postgres(connectionString, {
max: 10, // Adjust based on your needs
idle_timeout: 20,
connect_timeout: 10,
});
references/drizzle-boilerplate.md - Complete setup codereferences/migrations.md - Advanced migration patternstools
# Versioning Skill Semantic versioning automation based on conventional commits. Automatically manages version bumps, changelogs, and git tags using `standard-version`. ## When to Use - Before releasing a new version - When preparing a deployment - To generate/update CHANGELOG.md - When the user asks about version management - Setting up versioning for a new project ## Prerequisites - Conventional commits enforced (recommended: lefthook) - Node.js project with package.json ## Setup (One-Ti
tools
Theme generation with tweakcn for shadcn/ui and Magic UI animations. Use when setting up project themes, customizing color schemes, adding dark mode, or integrating animated components.
tools
shadcn/studio component library with MCP integration, theme generation, and block patterns. This skill should be used when building UI with shadcn components, selecting dashboard layouts, or generating landing pages. Canonical source for all shadcn-based work.
development
Enforce a precise, minimal design system inspired by Linear, Notion, and Stripe. Use this skill when building dashboards, admin interfaces, or any UI that needs Jony Ive-level precision - clean, modern, minimalist with taste. Every pixel matters.