database-schema by alinaqi/claude-bootstrap
npx skills add https://github.com/alinaqi/claude-bootstrap --skill database-schema加载方式:base.md + [你的数据库技能]
问题: Claude 在会话中忘记模式细节 - 错误的列名、缺失的字段、不正确的类型。TDD 在运行时能捕获这些问题,但我们可以更早地预防。
强制要求:在编写任何涉及数据库的代码之前:
┌─────────────────────────────────────────────────────────────┐
│ 1. 读取模式文件(见下方位置) │
│ 2. 验证你即将使用的列/类型是否存在 │
│ 3. 在编写查询时,在响应中引用模式 │
│ 4. 使用生成的类型进行类型检查(Drizzle/Prisma 等) │
└─────────────────────────────────────────────────────────────┘
如果模式文件不存在 → 在继续之前创建它。
| 技术栈 | 模式位置 | 类型生成 |
|---|---|---|
| Drizzle | src/db/schema.ts 或 drizzle/schema.ts |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 内置 TypeScript |
| Prisma | prisma/schema.prisma | npx prisma generate |
| Supabase | supabase/migrations/*.sql + 类型 | supabase gen types typescript |
| SQLAlchemy | app/models/*.py 或 src/models.py | Pydantic 模型 |
| TypeORM | src/entities/*.ts | 装饰器 = 类型 |
| 原始 SQL | schema.sql 或 migrations/ | 需要手动定义类型 |
创建 _project_specs/schema-reference.md 以便快速查阅:
# 数据库模式参考
*自动生成或手动维护。Claude:在进行数据库工作前先阅读此文件。*
## 表
### users
| 列名 | 类型 | 可空 | 默认值 | 备注 |
|--------|------|----------|---------|-------|
| id | uuid | NO | gen_random_uuid() | 主键 |
| email | text | NO | - | 唯一 |
| name | text | YES | - | 显示名称 |
| created_at | timestamptz | NO | now() | - |
| updated_at | timestamptz | NO | now() | - |
### orders
| 列名 | 类型 | 可空 | 默认值 | 备注 |
|--------|------|----------|---------|-------|
| id | uuid | NO | gen_random_uuid() | 主键 |
| user_id | uuid | NO | - | 外键 → users.id |
| status | text | NO | 'pending' | 枚举: pending/paid/shipped/delivered |
| total_cents | integer | NO | - | 金额(单位:分) |
| created_at | timestamptz | NO | now() | - |
## 关系
- users 1:N orders (user_id)
## 枚举
- order_status: pending, paid, shipped, delivered
在编写任何数据库代码之前,Claude 必须:
### 模式验证检查清单
- [ ] 读取模式文件:`[模式文件路径]`
- [ ] 我使用的列存在:[列出列名]
- [ ] 类型与我的代码匹配:[列出类型映射]
- [ ] 关系正确:[列出外键]
- [ ] 可空字段已处理:[列出可空列]
实践示例:
### TODO-042 的模式验证(添加订单历史端点)
- [x] 读取模式:`src/db/schema.ts`
- [x] 列存在:orders.id, orders.user_id, orders.status, orders.total_cents, orders.created_at
- [x] 类型:id=uuid→string, total_cents=integer→number, status=text→OrderStatus 枚举
- [x] 关系:orders.user_id → users.id (多对一)
- [x] 可空性:这些列都不可空
// 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;
// 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")
}
# Generate types after schema changes
npx prisma generate
# Generate TypeScript types from live database
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
// 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'];
# 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")
# app/schemas/user.py - Pydantic for API validation
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
为数据库工作扩展标准 TDD 工作流:
┌─────────────────────────────────────────────────────────────┐
│ 0. 模式:在做任何其他事情之前,先读取并验证模式 │
│ └─ 读取模式文件 │
│ └─ 完成模式验证检查清单 │
│ └─ 记录任何缺失的列/表 │
├─────────────────────────────────────────────────────────────┤
│ 1. 红:编写使用正确列名的测试 │
│ └─ 导入生成的类型 │
│ └─ 在测试中使用类型安全的查询 │
│ └─ 测试应因逻辑失败,而非模式错误 │
├─────────────────────────────────────────────────────────────┤
│ 2. 绿:使用类型安全的查询实现功能 │
│ └─ 使用 ORM 类型,而非原始字符串 │
│ └─ TypeScript/mypy 捕获列不匹配 │
├─────────────────────────────────────────────────────────────┤
│ 3. 验证:类型检查捕获模式漂移 │
│ └─ tsc --noEmit / mypy 捕获错误的列 │
│ └─ 测试验证运行时行为 │
└─────────────────────────────────────────────────────────────┘
| 错误 | 示例 | 预防方法 |
|---|---|---|
| 错误的列名 | user.userName 对比 user.name | 读取模式,使用生成的类型 |
| 错误的类型 | totalCents 作为字符串 | 类型生成会捕获此错误 |
| 缺失可空性检查 | user.name! 当列可空时 | 模式显示可空字段 |
| 错误的外键关系 | order.userId 对比 order.user_id | 检查模式中的列名 |
| 缺失的列 | 使用不存在的 user.avatar | 编码前读取模式 |
| 错误的枚举值 | status: 'complete' 对比 'completed' | 在模式参考中记录枚举 |
Drizzle(在编译时捕获错误):
// ✅ 正确 - 使用模式定义的列
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(在编译时捕获错误):
// ✅ 正确
const user = await prisma.user.findUnique({ where: { email } });
// ❌ 错误 - TypeScript 错误
const user = await prisma.user.findUnique({ where: { userName: email } });
原始 SQL(无保护 - 避免使用):
// ❌ 危险 - 无类型检查,容易出错
const result = await db.query('SELECT * FROM users WHERE user_name = $1', [email]);
// 应该是 'email' 而不是 'user_name' - 直到运行时才会捕获
当需要更改模式时:
┌─────────────────────────────────────────────────────────────┐
│ 1. 更新模式文件(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. 修复类型错误,更新测试,运行完整验证 │
└─────────────────────────────────────────────────────────────┘
当开始涉及数据库工作的会话时:
_project_specs/schema-reference.md会话状态示例:
## 当前会话 - 数据库上下文
**模式已读:** ✓ 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
_project_specs/schema-reference.md 已创建每周安装量
104
仓库
GitHub 星标数
538
首次出现
2026年1月20日
安全审计
安装于
gemini-cli86
opencode86
claude-code84
cursor80
codex80
github-copilot70
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.
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.
| Stack | Schema Location | Type Generation |
|---|---|---|
| Drizzle | src/db/schema.ts or drizzle/schema.ts | Built-in TypeScript |
| Prisma | prisma/schema.prisma | npx prisma generate |
| Supabase | supabase/migrations/*.sql + types | supabase gen types typescript |
| SQLAlchemy | app/models/*.py or src/models.py | Pydantic models |
| TypeORM | src/entities/*.ts | Decorators = types |
| Raw SQL | schema.sql or migrations/ | Manual types required |
Create _project_specs/schema-reference.md for quick lookup:
# Database Schema Reference
*Auto-generated or manually maintained. Claude: READ THIS before database work.*
## Tables
### users
| Column | Type | Nullable | Default | Notes |
|--------|------|----------|---------|-------|
| id | uuid | NO | gen_random_uuid() | PK |
| email | text | NO | - | Unique |
| name | text | YES | - | Display name |
| created_at | timestamptz | NO | now() | - |
| updated_at | timestamptz | NO | now() | - |
### 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() | - |
## Relationships
- users 1:N orders (user_id)
## Enums
- order_status: pending, paid, shipped, delivered
Before writing any database code, Claude MUST:
### Schema Verification Checklist
- [ ] 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:
### Schema Verification for TODO-042 (Add order history endpoint)
- [x] Read schema: `src/db/schema.ts`
- [x] Columns exist: orders.id, orders.user_id, orders.status, orders.total_cents, orders.created_at
- [x] Types: id=uuid→string, total_cents=integer→number, status=text→OrderStatus enum
- [x] Relationships: orders.user_id → users.id (many-to-one)
- [x] Nullable: none of these columns are nullable
// 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;
// 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")
}
# Generate types after schema changes
npx prisma generate
# Generate TypeScript types from live database
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
// 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'];
# 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")
# app/schemas/user.py - Pydantic for API validation
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
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 │
└─────────────────────────────────────────────────────────────┘
| Mistake | Example | Prevention |
|---|---|---|
| Wrong column name | user.userName vs user.name | Read schema, use generated types |
| Wrong type | totalCents as string | Type generation catches this |
| Missing nullable check | user.name! when nullable | Schema shows nullable fields |
| Wrong FK relationship | order.userId vs order.user_id |
Drizzle (catches errors at compile time):
// ✅ 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):
// ✅ 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):
// ❌ 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 runtime
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 │
└─────────────────────────────────────────────────────────────┘
When starting a session that involves database work:
_project_specs/schema-reference.md if existsSession state example:
## Current 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
_project_specs/schema-reference.md createdWeekly Installs
104
Repository
GitHub Stars
538
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykPass
Installed on
gemini-cli86
opencode86
claude-code84
cursor80
codex80
github-copilot70
通过 LiteLLM 代理让 Claude Code 对接 GitHub Copilot 运行 | 高级变通方案指南
44,900 周安装
CCG:Claude-Codex-Gemini三模型协同AI代码助手,实现并行代码审查与开发
157 周安装
Web Artifacts Builder:React + TypeScript + Vite 前端工件构建工具,一键打包为单HTML文件
154 周安装
Bitbucket工作流最佳实践:拉取请求、Pipelines CI/CD与Jira集成完整指南
163 周安装
Java Gradle 构建工具指南:Kotlin DSL 配置、依赖管理与性能优化
157 周安装
市场广度分析工具 - 量化评分系统,评估股市健康状况与参与广度
162 周安装
MLflow Python技能:统一机器学习实验追踪与量化交易指标分析工具
72 周安装
| Check schema column names |
| Missing column | Using user.avatar that doesn't exist | Read schema before coding |
| Wrong enum value | status: 'complete' vs 'completed' | Document enums in schema reference |