database-schema
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDatabase Schema Awareness Skill
数据库Schema感知技能
Load with: base.md + [your database skill]
Problem: Claude forgets schema details mid-session - wrong column names, missing fields, incorrect types. TDD catches this at runtime, but we can prevent it earlier.
加载方式:base.md + [你的数据库技能]
问题: Claude在会话过程中会忘记Schema细节——列名错误、字段缺失、类型不正确。TDD会在运行时发现这些问题,但我们可以更早地预防。
Core Rule: Read Schema Before Writing Database Code
核心规则:编写数据库代码前先查阅Schema
MANDATORY: Before writing ANY code that touches the database:
┌─────────────────────────────────────────────────────────────┐
│ 1. READ the schema file (see locations below) │
│ 2. VERIFY columns/types you're about to use exist │
│ 3. REFERENCE schema in your response when writing queries │
│ 4. TYPE-CHECK using generated types (Drizzle/Prisma/etc) │
└─────────────────────────────────────────────────────────────┘If schema file doesn't exist → CREATE IT before proceeding.
强制要求:在编写任何涉及数据库的代码前:
┌─────────────────────────────────────────────────────────────┐
│ 1. 查阅Schema文件(见下方位置) │
│ 2. 验证你要使用的列/类型是否存在 │
│ 3. 编写查询时在响应中引用Schema │
│ 4. 使用生成的类型进行类型检查(Drizzle/Prisma等) │
└─────────────────────────────────────────────────────────────┘如果Schema文件不存在 → 先创建它再继续。
Schema File Locations (By Stack)
Schema文件位置(按技术栈分类)
| Stack | Schema Location | Type Generation |
|---|---|---|
| Drizzle | | Built-in TypeScript |
| Prisma | | |
| Supabase | | |
| SQLAlchemy | | Pydantic models |
| TypeORM | | Decorators = types |
| Raw SQL | | Manual types required |
| 技术栈 | Schema位置 | 类型生成 |
|---|---|---|
| Drizzle | | 内置TypeScript类型 |
| Prisma | | |
| Supabase | | |
| SQLAlchemy | | Pydantic模型 |
| TypeORM | | 装饰器定义类型 |
| 原生SQL | | 需要手动定义类型 |
Schema Reference File (Recommended)
Schema参考文件(推荐)
Create for quick lookup:
_project_specs/schema-reference.mdmarkdown
undefined创建以便快速查阅:
_project_specs/schema-reference.mdmarkdown
undefinedDatabase Schema Reference
数据库Schema参考
Auto-generated or manually maintained. Claude: READ THIS before database work.
自动生成或手动维护。Claude:进行数据库操作前请阅读此文件。
Tables
表
users
users
| Column | Type | Nullable | Default | Notes |
|---|---|---|---|---|
| id | uuid | NO | gen_random_uuid() | PK |
| text | NO | - | Unique | |
| name | text | YES | - | Display name |
| created_at | timestamptz | NO | now() | - |
| updated_at | timestamptz | NO | now() | - |
| 列 | 类型 | 是否可为空 | 默认值 | 说明 |
|---|---|---|---|---|
| id | uuid | 否 | gen_random_uuid() | 主键 |
| text | 否 | - | 唯一 | |
| name | text | 是 | - | 显示名称 |
| created_at | timestamptz | 否 | now() | - |
| updated_at | timestamptz | 否 | now() | - |
orders
orders
| Column | Type | Nullable | Default | Notes |
|---|---|---|---|---|
| id | uuid | NO | gen_random_uuid() | PK |
| user_id | uuid | NO | - | FK → users.id |
| status | text | NO | 'pending' | enum: pending/paid/shipped/delivered |
| total_cents | integer | NO | - | Amount in cents |
| created_at | timestamptz | NO | now() | - |
| 列 | 类型 | 是否可为空 | 默认值 | 说明 |
|---|---|---|---|---|
| id | uuid | 否 | gen_random_uuid() | 主键 |
| user_id | uuid | 否 | - | 外键 → users.id |
| status | text | 否 | 'pending' | 枚举:pending/paid/shipped/delivered |
| total_cents | integer | 否 | - | 金额(单位:分) |
| created_at | timestamptz | 否 | now() | - |
Relationships
关系
- users 1:N orders (user_id)
- users 一对多 orders(通过user_id)
Enums
枚举
- order_status: pending, paid, shipped, delivered
---- order_status: pending, paid, shipped, delivered
---Pre-Code Checklist (Database Work)
编码前检查清单(数据库操作)
Before writing any database code, Claude MUST:
markdown
undefined在编写任何数据库代码前,Claude必须:
markdown
undefinedSchema Verification Checklist
Schema验证检查清单
- Read schema file:
[path to schema] - Columns I'm using exist: [list columns]
- Types match my code: [list type mappings]
- Relationships are correct: [list FKs]
- Nullable fields handled: [list nullable columns]
**Example in practice:**
```markdown- 已查阅Schema文件:
[Schema路径] - 我要使用的列存在:[列出列名]
- 类型与我的代码匹配:[列出类型映射]
- 关系设置正确:[列出外键]
- 可为空字段已处理:[列出可为空列]
**实际示例:**
```markdownSchema Verification for TODO-042 (Add order history endpoint)
TODO-042(添加订单历史接口)的Schema验证
- Read schema:
src/db/schema.ts - Columns exist: orders.id, orders.user_id, orders.status, orders.total_cents, orders.created_at
- Types: id=uuid→string, total_cents=integer→number, status=text→OrderStatus enum
- Relationships: orders.user_id → users.id (many-to-one)
- Nullable: none of these columns are nullable
---- 已查阅Schema:
src/db/schema.ts - 列存在:orders.id, orders.user_id, orders.status, orders.total_cents, orders.created_at
- 类型:id=uuid→string, total_cents=integer→number, status=text→OrderStatus枚举
- 关系:orders.user_id → users.id(多对一)
- 可为空:这些列均不可为空
---Type Generation Commands
类型生成命令
Drizzle (TypeScript)
Drizzle(TypeScript)
typescript
// Schema defines types automatically
// src/db/schema.ts
import { pgTable, uuid, text, integer, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
name: text('name'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const orders = pgTable('orders', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').notNull().references(() => users.id),
status: text('status').notNull().default('pending'),
totalCents: integer('total_cents').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// Inferred types - USE THESE
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Order = typeof orders.$inferSelect;
export type NewOrder = typeof orders.$inferInsert;typescript
// Schema自动定义类型
// src/db/schema.ts
import { pgTable, uuid, text, integer, timestamp } from 'drizzle-orm/pg-core';
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: text('email').notNull().unique(),
name: text('name'),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
export const orders = pgTable('orders', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').notNull().references(() => users.id),
status: text('status').notNull().default('pending'),
totalCents: integer('total_cents').notNull(),
createdAt: timestamp('created_at').notNull().defaultNow(),
});
// 推断类型 - 请使用这些类型
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type Order = typeof orders.$inferSelect;
export type NewOrder = typeof orders.$inferInsert;Prisma
Prisma
prisma
// prisma/schema.prisma
model User {
id String @id @default(uuid())
email String @unique
name String?
orders Order[]
createdAt DateTime @default(now()) @map("created_at")
@@map("users")
}
model Order {
id String @id @default(uuid())
userId String @map("user_id")
user User @relation(fields: [userId], references: [id])
status String @default("pending")
totalCents Int @map("total_cents")
createdAt DateTime @default(now()) @map("created_at")
@@map("orders")
}bash
undefinedprisma
// prisma/schema.prisma
model User {
id String @id @default(uuid())
email String @unique
name String?
orders Order[]
createdAt DateTime @default(now()) @map("created_at")
@@map("users")
}
model Order {
id String @id @default(uuid())
userId String @map("user_id")
user User @relation(fields: [userId], references: [id])
status String @default("pending")
totalCents Int @map("total_cents")
createdAt DateTime @default(now()) @map("created_at")
@@map("orders")
}bash
undefinedGenerate types after schema changes
Schema变更后生成类型
npx prisma generate
undefinednpx prisma generate
undefinedSupabase
Supabase
bash
undefinedbash
undefinedGenerate TypeScript types from live database
从本地数据库生成TypeScript类型
supabase gen types typescript --local > src/types/database.ts
supabase gen types typescript --local > src/types/database.ts
Or from remote
或从远程数据库生成
supabase gen types typescript --project-id your-project-id > src/types/database.ts
```typescript
// Use generated types
import { Database } from '@/types/database';
type User = Database['public']['Tables']['users']['Row'];
type NewUser = Database['public']['Tables']['users']['Insert'];
type Order = Database['public']['Tables']['orders']['Row'];supabase gen types typescript --project-id your-project-id > src/types/database.ts
```typescript
// 使用生成的类型
import { Database } from '@/types/database';
type User = Database['public']['Tables']['users']['Row'];
type NewUser = Database['public']['Tables']['users']['Insert'];
type Order = Database['public']['Tables']['orders']['Row'];SQLAlchemy (Python)
SQLAlchemy(Python)
python
undefinedpython
undefinedapp/models/user.py
app/models/user.py
from sqlalchemy import Column, String, DateTime
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import func
from app.db import Base
import uuid
class User(Base):
tablename = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String, nullable=False, unique=True)
name = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
orders = relationship("Order", back_populates="user")
```pythonfrom sqlalchemy import Column, String, DateTime
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import func
from app.db import Base
import uuid
class User(Base):
tablename = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String, nullable=False, unique=True)
name = Column(String, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
# 关系
orders = relationship("Order", back_populates="user")
```pythonapp/schemas/user.py - Pydantic for API validation
app/schemas/user.py - 用于API验证的Pydantic模型
from pydantic import BaseModel, EmailStr
from uuid import UUID
from datetime import datetime
class UserBase(BaseModel):
email: EmailStr
name: str | None = None
class UserCreate(UserBase):
pass
class User(UserBase):
id: UUID
created_at: datetime
class Config:
from_attributes = True
---from pydantic import BaseModel, EmailStr
from uuid import UUID
from datetime import datetime
class UserBase(BaseModel):
email: EmailStr
name: str | None = None
class UserCreate(UserBase):
pass
class User(UserBase):
id: UUID
created_at: datetime
class Config:
from_attributes = True
---Schema-Aware TDD Workflow
支持Schema感知的TDD工作流
Extend the standard TDD workflow for database work:
┌─────────────────────────────────────────────────────────────┐
│ 0. SCHEMA: Read and verify schema before anything else │
│ └─ Read schema file │
│ └─ Complete Schema Verification Checklist │
│ └─ Note any missing columns/tables needed │
├─────────────────────────────────────────────────────────────┤
│ 1. RED: Write tests that use correct column names │
│ └─ Import generated types │
│ └─ Use type-safe queries in tests │
│ └─ Tests should fail on logic, NOT schema errors │
├─────────────────────────────────────────────────────────────┤
│ 2. GREEN: Implement with type-safe queries │
│ └─ Use ORM types, not raw strings │
│ └─ TypeScript/mypy catches column mismatches │
├─────────────────────────────────────────────────────────────┤
│ 3. VALIDATE: Type check catches schema drift │
│ └─ tsc --noEmit / mypy catches wrong columns │
│ └─ Tests validate runtime behavior │
└─────────────────────────────────────────────────────────────┘为数据库操作扩展标准TDD工作流:
┌─────────────────────────────────────────────────────────────┐
│ 0. SCHEMA:先查阅并验证Schema,再进行其他操作 │
│ └─ 查阅Schema文件 │
│ └─ 完成Schema验证检查清单 │
│ └─ 记录所需的缺失列/表 │
├─────────────────────────────────────────────────────────────┤
│ 1. 红:编写使用正确列名的测试 │
│ └─ 导入生成的类型 │
│ └─ 在测试中使用类型安全的查询 │
│ └─ 测试应因逻辑失败,而非Schema错误 │
├─────────────────────────────────────────────────────────────┤
│ 2. 绿:使用类型安全的查询实现功能 │
│ └─ 使用ORM类型,而非原生字符串 │
│ └─ TypeScript/mypy会捕获列不匹配问题 │
├─────────────────────────────────────────────────────────────┤
│ 3. 验证:类型检查捕获Schema偏移 │
│ └─ tsc --noEmit / mypy会捕获错误的列名 │
│ └─ 测试验证运行时行为 │
└─────────────────────────────────────────────────────────────┘Common Schema Mistakes (And How to Prevent)
常见Schema错误(及预防方法)
| Mistake | Example | Prevention |
|---|---|---|
| Wrong column name | | Read schema, use generated types |
| Wrong type | | Type generation catches this |
| Missing nullable check | | Schema shows nullable fields |
| Wrong FK relationship | | Check schema column names |
| Missing column | Using | Read schema before coding |
| Wrong enum value | | Document enums in schema reference |
| 错误 | 示例 | 预防方法 |
|---|---|---|
| 列名错误 | | 查阅Schema,使用生成的类型 |
| 类型错误 | 将 | 类型生成会捕获此问题 |
| 忽略可为空检查 | 当 | Schema会显示可为空字段 |
| 外键关系错误 | | 检查Schema中的列名 |
| 使用不存在的列 | 使用不存在的 | 编码前查阅Schema |
| 枚举值错误 | | 在Schema参考文件中记录枚举 |
Type-Safe Query Examples
类型安全查询示例
Drizzle (catches errors at compile time):
typescript
// ✅ Correct - uses schema-defined columns
const user = await db.select().from(users).where(eq(users.email, email));
// ❌ Wrong - TypeScript error: 'userName' doesn't exist
const user = await db.select().from(users).where(eq(users.userName, email));Prisma (catches errors at compile time):
typescript
// ✅ Correct
const user = await prisma.user.findUnique({ where: { email } });
// ❌ Wrong - TypeScript error
const user = await prisma.user.findUnique({ where: { userName: email } });Raw SQL (NO protection - avoid):
typescript
// ❌ Dangerous - no type checking, easy to get wrong
const result = await db.query('SELECT * FROM users WHERE user_name = $1', [email]);
// Should be 'email' not 'user_name' - won't catch until runtimeDrizzle(编译时捕获错误):
typescript
// ✅ 正确 - 使用Schema定义的列
const user = await db.select().from(users).where(eq(users.email, email));
// ❌ 错误 - TypeScript报错:'userName'不存在
const user = await db.select().from(users).where(eq(users.userName, email));Prisma(编译时捕获错误):
typescript
// ✅ 正确
const user = await prisma.user.findUnique({ where: { email } });
// ❌ 错误 - TypeScript报错
const user = await prisma.user.findUnique({ where: { userName: email } });原生SQL(无保护 - 避免使用):
typescript
// ❌ 危险 - 无类型检查,容易出错
const result = await db.query('SELECT * FROM users WHERE user_name = $1', [email]);
// 应为'email'而非'user_name' - 运行时才会发现错误Migration Workflow
迁移工作流
When schema changes are needed:
┌─────────────────────────────────────────────────────────────┐
│ 1. Update schema file (Drizzle/Prisma/SQLAlchemy) │
├─────────────────────────────────────────────────────────────┤
│ 2. Generate migration │
│ └─ Drizzle: npx drizzle-kit generate │
│ └─ Prisma: npx prisma migrate dev --name add_column │
│ └─ Supabase: supabase migration new add_column │
├─────────────────────────────────────────────────────────────┤
│ 3. Regenerate types │
│ └─ Prisma: npx prisma generate │
│ └─ Supabase: supabase gen types typescript │
├─────────────────────────────────────────────────────────────┤
│ 4. Update schema-reference.md │
├─────────────────────────────────────────────────────────────┤
│ 5. Run type check - find all broken code │
│ └─ npm run typecheck │
├─────────────────────────────────────────────────────────────┤
│ 6. Fix type errors, update tests, run full validation │
└─────────────────────────────────────────────────────────────┘当需要变更Schema时:
┌─────────────────────────────────────────────────────────────┐
│ 1. 更新Schema文件(Drizzle/Prisma/SQLAlchemy) │
├─────────────────────────────────────────────────────────────┤
│ 2. 生成迁移文件 │
│ └─ Drizzle: npx drizzle-kit generate │
│ └─ Prisma: npx prisma migrate dev --name add_column │
│ └─ Supabase: supabase migration new add_column │
├─────────────────────────────────────────────────────────────┤
│ 3. 重新生成类型 │
│ └─ Prisma: npx prisma generate │
│ └─ Supabase: supabase gen types typescript │
├─────────────────────────────────────────────────────────────┤
│ 4. 更新schema-reference.md │
├─────────────────────────────────────────────────────────────┤
│ 5. 运行类型检查 - 找出所有失效代码 │
│ └─ npm run typecheck │
├─────────────────────────────────────────────────────────────┤
│ 6. 修复类型错误,更新测试,运行完整验证 │
└─────────────────────────────────────────────────────────────┘Session Start Protocol
会话启动协议
When starting a session that involves database work:
- Read schema file immediately
- Read if exists
_project_specs/schema-reference.md - Note in session state what tables/columns are relevant
- Reference schema explicitly when writing code
Session state example:
markdown
undefined当启动涉及数据库操作的会话时:
- 立即查阅Schema文件
- 如果存在则查阅它
_project_specs/schema-reference.md - 在会话状态中记录相关的表/列
- 编写代码时明确引用Schema
会话状态示例:
markdown
undefinedCurrent Session - Database Context
当前会话 - 数据库上下文
Schema read: ✓ src/db/schema.ts
Tables in scope: users, orders, order_items
Key columns:
- users: id, email, name, created_at
- orders: id, user_id, status, total_cents
- order_items: id, order_id, product_id, quantity, price_cents
---已查阅Schema:✓ src/db/schema.ts
涉及的表:users, orders, order_items
关键列:
- users: id, email, name, created_at
- orders: id, user_id, status, total_cents
- order_items: id, order_id, product_id, quantity, price_cents
---Anti-Patterns
反模式
- ❌ Guessing column names - Always read schema first
- ❌ Using raw SQL strings - Use ORM with type generation
- ❌ Hardcoding without verification - Check schema before using any column
- ❌ Ignoring type errors - Schema drift shows up as type errors
- ❌ Not regenerating types - After migration, always regenerate
- ❌ Assuming nullable - Check schema for nullable columns
- ❌ 猜测列名 - 始终先查阅Schema
- ❌ 使用原生SQL字符串 - 使用带类型生成的ORM
- ❌ 未验证就硬编码 - 使用任何列前都要检查Schema
- ❌ 忽略类型错误 - Schema偏移会表现为类型错误
- ❌ 不重新生成类型 - 迁移后务必重新生成类型
- ❌ 默认认为字段可为空 - 检查Schema中的可为空列
Checklist
检查清单
Setup
配置
- Schema file exists in standard location
- Type generation configured
- created
_project_specs/schema-reference.md - Types regenerate on schema change
- Schema文件存在于标准位置
- 已配置类型生成
- 已创建
_project_specs/schema-reference.md - Schema变更时会重新生成类型
Per-Task
每个任务
- Schema read before writing database code
- Schema Verification Checklist completed
- Using generated types (not raw strings)
- Type check passes (catches column errors)
- Tests use correct schema
- 编写数据库代码前已查阅Schema
- 已完成Schema验证检查清单
- 使用生成的类型(而非原生字符串)
- 类型检查通过(捕获列错误)
- 测试使用正确的Schema