当然, 我很乐意为您详细展开第 2 部分”数据库设计和 ORM 设置”的内容。这个部分我们将分为 2 天来完成, 每天的任务都会非常具体。
第 2 部分: 数据库设计和 ORM 设置 (2天)
Day 1: 数据模型设计和 Drizzle ORM 配置
-
设置 Drizzle ORM 配置文件 创建文件
drizzle.config.ts:import type { Config } from 'drizzle-kit'; import { config } from './src/config'; export default { schema: './src/models/*', out: './drizzle', driver: 'turso', dbCredentials: { url: config.databaseUrl, }, } satisfies Config; -
创建用户模型 在
src/models/user.ts中:import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; export const users = sqliteTable('users', { id: text('id').primaryKey(), username: text('username').notNull().unique(), email: text('email').notNull().unique(), passwordHash: text('password_hash').notNull(), createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`), }); export type User = typeof users.$inferSelect; export type NewUser = typeof users.$inferInsert; -
创建 Todo 模型 在
src/models/todo.ts中:import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; import { users } from './user'; export const todos = sqliteTable('todos', { id: text('id').primaryKey(), title: text('title').notNull(), description: text('description'), completed: integer('completed', { mode: 'boolean' }).notNull().default(false), priority: integer('priority').notNull().default(1), dueDate: integer('due_date', { mode: 'timestamp' }), userId: text('user_id').notNull().references(() => users.id), createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`), updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`), }); export type Todo = typeof todos.$inferSelect; export type NewTodo = typeof todos.$inferInsert; -
创建标签模型 在
src/models/tag.ts中:import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'; import { users } from './user'; export const tags = sqliteTable('tags', { id: text('id').primaryKey(), name: text('name').notNull(), userId: text('user_id').notNull().references(() => users.id), createdAt: integer('created_at', { mode: 'timestamp' }).notNull().default(sql`CURRENT_TIMESTAMP`), }); export const todoTags = sqliteTable('todo_tags', { id: text('id').primaryKey(), todoId: text('todo_id').notNull().references(() => todos.id), tagId: text('tag_id').notNull().references(() => tags.id), }); export type Tag = typeof tags.$inferSelect; export type NewTag = typeof tags.$inferInsert; -
设置数据库关系 创建
src/models/index.ts:export * from './user'; export * from './todo'; export * from './tag'; import { relations } from 'drizzle-orm'; import { users, todos, tags, todoTags } from './'; export const usersRelations = relations(users, ({ many }) => ({ todos: many(todos), tags: many(tags), })); export const todosRelations = relations(todos, ({ one, many }) => ({ user: one(users, { fields: [todos.userId], references: [users.id] }), tags: many(todoTags), })); export const tagsRelations = relations(tags, ({ one, many }) => ({ user: one(users, { fields: [tags.userId], references: [users.id] }), Todos: many (todoTags), })); Export const todoTagsRelations = relations (todoTags, ({ one }) => ({ todo: one(todos, { fields: [todoTags.todoId], references: [todos.id] }), tag: one(tags, { fields: [todoTags.tagId], references: [tags.id] }), }));
Day 2: 数据库连接和迁移
-
设置 Turso 数据库连接 更新
src/db.ts:import { createClient } from '@libsql/client'; import { drizzle } from 'drizzle-orm/libsql'; import { config } from './config'; import * as schema from './models'; const client = createClient({ url: config.databaseUrl, authToken: config.databaseAuthToken, }); export const db = drizzle(client, { schema }); -
创建数据库迁移脚本 在
package.json中添加新的脚本:{ "scripts": { "db:generate": "drizzle-kit generate:sqlite", "db:push": "drizzle-kit push:sqlite", "db:studio": "drizzle-kit studio" } } -
生成初始迁移 运行命令:
bun run db:generate这将在
drizzle目录下创建迁移文件。 -
应用迁移到数据库 运行命令:
bun run db:push -
创建数据库种子脚本 创建文件
scripts/seed.ts:import { db } from '../src/db'; import { users, todos, tags, todoTags } from '../src/models'; import { v4 as uuidv4 } from 'uuid'; import * as bcrypt from 'bcrypt'; async function seed() { // 创建示例用户 const userId = uuidv4(); await db.insert(users).values({ id: userId, username: 'testuser', email: 'test@example.com', passwordHash: await bcrypt.hash('password123', 10), }); // 创建示例Todo const todoId = uuidv4(); await db.insert(todos).values({ id: todoId, title: 'Complete project', description: 'Finish the LightweightTodo Pro project', userId: userId, priority: 2, dueDate: new Date('2023-12-31'), }); // 创建示例标签 const tagId = uuidv4(); await db.insert(tags).values({ id: tagId, name: 'Work', userId: userId, }); // 关联Todo和标签 await db.insert(todoTags).values({ id: uuidv4(), todoId: todoId, tagId: tagId, }); console.log('Seed data inserted successfully'); } seed().catch(console.error); -
添加种子脚本到
package.json:{ "scripts": { "db:seed": "bun run scripts/seed.ts" } } -
运行种子脚本:
bun run db:seed -
创建数据库工具函数 在
src/utils/db.ts中:import { db } from '../db'; import { SQL } from 'drizzle-orm'; export async function executeTransaction<T>(callback: () => Promise<T>): Promise<T> { return db.transaction(async (tx) => { return await callback(); }); } export async function executeQuery<T>(query: SQL): Promise<T[]> { Return db.Select (). From (query); }
这个详细的计划涵盖了数据库设计、ORM 配置、迁移管理和初始数据填充。它为您的 LightweightTodo Pro 项目建立了一个强大的数据层基础。主要亮点包括:
- 使用 Drizzle ORM 定义了清晰的数据模型和关系。
- 设置了与 Turso 数据库的连接。
- 创建了数据库迁移和种子脚本,便于管理数据库结构和初始数据。
- 提供了事务和查询的工具函数,简化了数据库操作。
这个设置为接下来的后端 API 开发和前端集成奠定了坚实的基础。在下一部分中,我们将基于这个数据层开始构建 API 端点和业务逻辑。