使用EdgeDB和Drizzle构建书籍笔记应用教程
概述
本教程将指导您使用EdgeDB、Drizzle ORM和Next.js构建一个完整的书籍笔记应用。这个应用允许用户记录阅读过的书籍,并为每本书添加个人笔记。我们将采用现代技术栈,结合EdgeDB的强大功能和Drizzle ORM的类型安全特性。
技术栈介绍
EdgeDB
EdgeDB是一个基于PostgreSQL的图式关系数据库,它提供了:
- 对象-关系映射的直观体验
- 内置的访问控制和认证功能
- 强大的EdgeQL查询语言
- 自动解决N+1查询问题
- 多语言支持
Drizzle ORM
Drizzle是一个轻量级TypeScript ORM,特点包括:
- 完全类型安全
- 简洁的API设计
- 支持多种数据库方言
- 自动生成类型定义
Next.js
作为React框架,Next.js提供了:
- 服务端组件支持
- 内置路由系统
- API路由功能
- 优秀的开发体验
项目初始化
1. 创建Next.js项目
首先使用create-next-app初始化项目:
npx create-next-app@latest book-notes-app
选择以下配置选项:
- TypeScript: 是
- ESLint: 是
- Tailwind CSS: 是
- src目录: 是
- App Router: 是
2. 安装EdgeDB依赖
npm i edgedb
3. 初始化EdgeDB项目
npx edgedb project init
数据库模式设计
在dbschema/default.esdl
中定义我们的数据模型:
module default {
type Book {
required title: str;
author: str;
year: int16;
genre: str;
read_date: datetime;
multi notes := .<book[is Note];
}
type Note {
required text: str;
created_at: datetime {
default := datetime_current();
}
required book: Book;
}
}
应用迁移:
edgedb migration create
edgedb migrate
集成Drizzle ORM
1. 安装Drizzle
npm i drizzle-orm drizzle-kit
2. 配置Drizzle
创建drizzle.config.ts
:
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
dialect: 'edgedb',
});
3. 拉取数据库模式
npx drizzle-kit pull
4. 配置自动同步
在edgedb.toml
中添加钩子:
[hooks]
after_migration_apply = [
"npx drizzle-kit pull"
]
创建数据库客户端
在src/db/index.ts
中设置数据库连接:
import { drizzle } from 'drizzle-orm/edgedb';
import { createClient } from 'edgedb';
import * as schema from '../../drizzle/schema';
import * as relations from '../../drizzle/relations';
const edgedbClient = createClient();
export const db = drizzle({
client: edgedbClient,
schema: {
...schema,
...relations,
}
});
// 类型定义
export type Book = typeof schema.book.$inferSelect;
export type NewBook = typeof schema.book.$inferInsert;
export interface BookWithNotes extends Book {
notes: Note[];
};
export type Note = typeof schema.note.$inferSelect;
export type NewNote = typeof schema.note.$inferInsert;
实现API路由
1. 书籍管理API
app/api/books/route.ts
:
import { NextResponse } from 'next/server';
import { db } from '@/src/db';
import { books } from '@/drizzle/schema';
export async function GET() {
try {
const allBooks = await db.query.book.findMany({
with: {
notes: true,
},
});
return NextResponse.json(allBooks);
} catch (error) {
return NextResponse.json(
{ error: '获取书籍列表失败' },
{ status: 500 }
);
}
}
export async function POST(request: Request) {
try {
const body = await request.json();
const result = await db.insert(book).values({
title: body.title,
author: body.author,
year: body.year,
genre: body.genre,
readDate: new Date(body.read_date),
}).returning();
return NextResponse.json(result[0], { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: '添加书籍失败' },
{ status: 500 }
);
}
}
2. 单个书籍操作API
app/api/books/[id]/route.ts
:
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
try {
const requestedBook = await db.query.book.findFirst({
where: eq(books.id, id),
with: {
note: true,
},
});
if (!requestedBook) {
return NextResponse.json(
{ error: '书籍未找到' },
{ status: 404 }
);
}
return NextResponse.json(requestedBook);
} catch (error) {
return NextResponse.json(
{ error: '获取书籍详情失败' },
{ status: 500 }
);
}
}
3. 笔记管理API
app/api/books/[id]/notes/route.ts
:
export async function POST(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;
try {
const body = await request.json();
const result = await db.insert(note).values({
text: body.text,
bookId: id,
}).returning();
return NextResponse.json(result[0], { status: 201 });
} catch (error) {
return NextResponse.json(
{ error: '添加笔记失败' },
{ status: 500 }
);
}
}
最佳实践
-
模式变更流程:
- 修改EdgeDB模式文件
- 创建并应用迁移
- 自动同步到Drizzle
-
错误处理:
- 统一错误响应格式
- 记录详细错误日志
- 返回用户友好的错误信息
-
类型安全:
- 充分利用Drizzle的类型推断
- 为常用查询结果定义接口类型
- 验证用户输入数据
总结
本教程展示了如何使用EdgeDB和Drizzle ORM构建一个全栈应用。EdgeDB提供了强大的数据建模能力,而Drizzle则带来了类型安全的数据库操作体验。这种组合特别适合需要复杂数据关系和类型安全性的应用场景。
通过本教程,您应该已经掌握了:
- EdgeDB的基本使用和模式设计
- Drizzle ORM的集成方法
- Next.js API路由的实现
- 现代全栈应用的架构设计
您可以根据这个基础继续扩展功能,如添加用户认证、实现更复杂的查询等。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考