Synthèse automatique
Fournit des modèles de base de données PostgreSQL avec Drizzle ORM, incluant des schémas, des repositories et des workflows de migration.
Points forts
- Encapsulation des requêtes via des repositories réutilisables
- Conventions claires pour les noms de tables et colonnes
- Gestion des migrations intégrée avec nommage standardisé
Limites
- Limité à PostgreSQL et Drizzle ORM
- Ne couvre pas les tests ou le seeding de données
- Les repositories génériques peuvent devenir verbeux pour des cas simples
Quand vous bâtissez une application avec Drizzle ORM et voulez une structure de couche d'accès aux données cohérente et maintenable.
Si la base de données est très simple et peut-être gérée directement, ou si vous utilisez un ORM différent.
Analyse de sécurité
SûrThe skill provides code patterns and best practices for Drizzle ORM usage. It contains no destructive, exfiltrating, or evasive instructions. All commands shown are standard development operations (e.g., migrations) and no sensitive data handling is demonstrated.
Aucun point d'attention détecté
Comment cette analyse est faiteExemples
Create a new repository for the 'comments' table following Drizzle ORM patterns with findById, create, update, delete methods.Generate a migration named 'add_category_to_posts' to add a text column 'category' to the posts table using Drizzle ORM.Database Skill (Drizzle ORM)
This skill provides database patterns for PostgreSQL with Drizzle ORM. Run /db for full reference.
Project Structure
src/db/
├── index.ts # Singleton db connection
├── schema.ts # All table definitions
├── migrate.ts # Migration runner
└── repositories/ # Data access layer (REQUIRED)
CRITICAL: Always Use Repositories
NEVER access db directly. Always use repository classes.
// ✅ CORRECT
import { featureRepository } from '@/db/repositories/feature.repository';
const feature = await featureRepository.findById(id);
// ❌ WRONG - Direct db access
import { db } from '@/db';
const feature = await db.select().from(features); // NEVER DO THIS
Schema Definition
// src/db/schema.ts
import { pgTable, uuid, text, timestamp, integer } from 'drizzle-orm/pg-core';
export const posts = pgTable('posts', {
id: uuid('id').defaultRandom().primaryKey(),
title: text('title').notNull(),
content: text('content'),
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }),
status: text('status').notNull().default('draft'),
priority: integer('priority').notNull().default(0),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
// Type exports
export type Post = typeof posts.$inferSelect;
export type NewPost = typeof posts.$inferInsert;
Schema Conventions
- Table names: lowercase plural (
users,posts) - Column names: snake_case (
created_at,user_id) - Primary keys: UUID with
defaultRandom() - Timestamps: Always include
created_atandupdated_at
Repository Pattern
// src/db/repositories/post.repository.ts
import { eq, and, desc, asc } from 'drizzle-orm';
import { db, schema } from '@/db';
import type { Post, NewPost } from '@/db/schema';
export class PostRepository {
async findById(id: string): Promise<Post | null> {
return await db.query.posts.findFirst({
where: eq(schema.posts.id, id),
}) ?? null;
}
async findMany(limit = 10, offset = 0): Promise<Post[]> {
return await db.query.posts.findMany({
limit,
offset,
orderBy: desc(schema.posts.createdAt),
});
}
async findByUserId(userId: string): Promise<Post[]> {
return await db.query.posts.findMany({
where: eq(schema.posts.userId, userId),
orderBy: (posts, { desc }) => [desc(posts.createdAt)],
});
}
async create(data: NewPost): Promise<Post> {
const result = await db
.insert(schema.posts)
.values({
...data,
createdAt: new Date(),
updatedAt: new Date(),
})
.returning();
return result[0];
}
async update(id: string, data: Partial<NewPost>): Promise<Post | null> {
const result = await db
.update(schema.posts)
.set({
...data,
updatedAt: new Date(),
})
.where(eq(schema.posts.id, id))
.returning();
return result[0] ?? null;
}
async delete(id: string): Promise<boolean> {
const result = await db
.delete(schema.posts)
.where(eq(schema.posts.id, id))
.returning();
return result.length > 0;
}
}
export const postRepository = new PostRepository();
Migration Workflow
# 1. Edit src/db/schema.ts
# 2. Generate migration
bun db:generate --name=create_posts_table
# 3. Review SQL in drizzle/migrations/
# 4. Apply migration
bun db:migrate
# 5. Commit migration files
git add drizzle/migrations/
Migration Naming
- Use snake_case:
create_posts_table,add_user_role_column - Start with action:
create_,add_,update_,remove_
Common Query Patterns
Filtering with Multiple Conditions
import { eq, and, or, isNull, isNotNull, gt, lt, like } from 'drizzle-orm';
// AND conditions
where: and(
eq(schema.posts.userId, userId),
eq(schema.posts.status, 'published')
)
// OR conditions
where: or(
eq(schema.posts.status, 'draft'),
eq(schema.posts.status, 'review')
)
// NULL checks
where: isNull(schema.posts.deletedAt)
// Comparisons
where: gt(schema.posts.priority, 5)
// LIKE
where: like(schema.posts.title, '%search%')
Ordering
// Single column
orderBy: desc(schema.posts.createdAt)
// Multiple columns
orderBy: (posts, { asc, desc }) => [
asc(posts.status),
desc(posts.priority),
desc(posts.createdAt),
]
Relations (with query API)
// In schema.ts - define relations
import { relations } from 'drizzle-orm';
export const postsRelations = relations(posts, ({ one, many }) => ({
author: one(users, {
fields: [posts.userId],
references: [users.id],
}),
comments: many(comments),
}));
// In repository - query with relations
async findWithAuthor(id: string) {
return await db.query.posts.findFirst({
where: eq(schema.posts.id, id),
with: {
author: true,
comments: true,
},
});
}
Database Client Setup
// src/db/index.ts
import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';
const client = postgres(process.env.DATABASE_URL!, {
max: process.env.NODE_ENV === 'production' ? 10 : 1,
idle_timeout: 20,
max_lifetime: 60 * 30,
});
export const db = drizzle(client, { schema });
export type Database = typeof db;
export { schema };
Common Imports
// Schema and types
import { db, schema } from '@/db';
import type { Post, NewPost } from '@/db/schema';
// Query operators
import { eq, and, or, gt, lt, gte, lte, like, isNull, isNotNull, desc, asc } from 'drizzle-orm';
// Table definitions (for insert/update)
import { pgTable, uuid, text, timestamp, integer, jsonb, boolean } from 'drizzle-orm/pg-core';
// Relations
import { relations } from 'drizzle-orm';
Specification Design-to-Dev
Developpement
Traduisez vos maquettes en specifications techniques pour les developpeurs.
Patterns d'exports et fichiers barrel
Developpement
Définit les conventions pour les exports de modules et les fichiers baril : exports nommés uniquement, pas de wildcards plats (sauf `export * as Name` pour les composants composés), commentaires par section, types exportés avec les valeurs, et une ligne d'export par fichier source. Aide à maintenir des exports cohérents et optimisés pour le tree-shaking dans les applications portal utilisant Turbopack ou des bundlers similaires.
Créer une compétence d'agent
Developpement
Guide étape par étape pour créer une compétence d'agent structurée, incluant structure de répertoire, format SKILL.md et principes de conception.