使用EdgeDB和Drizzle构建书籍笔记应用教程

使用EdgeDB和Drizzle构建书籍笔记应用教程

edgedb A graph-relational database with declarative schema, built-in migration system, and a next-generation query language edgedb 项目地址: https://gitcode.com/gh_mirrors/ed/edgedb

概述

本教程将指导您使用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 }
    );
  }
}

最佳实践

  1. 模式变更流程

    • 修改EdgeDB模式文件
    • 创建并应用迁移
    • 自动同步到Drizzle
  2. 错误处理

    • 统一错误响应格式
    • 记录详细错误日志
    • 返回用户友好的错误信息
  3. 类型安全

    • 充分利用Drizzle的类型推断
    • 为常用查询结果定义接口类型
    • 验证用户输入数据

总结

本教程展示了如何使用EdgeDB和Drizzle ORM构建一个全栈应用。EdgeDB提供了强大的数据建模能力,而Drizzle则带来了类型安全的数据库操作体验。这种组合特别适合需要复杂数据关系和类型安全性的应用场景。

通过本教程,您应该已经掌握了:

  1. EdgeDB的基本使用和模式设计
  2. Drizzle ORM的集成方法
  3. Next.js API路由的实现
  4. 现代全栈应用的架构设计

您可以根据这个基础继续扩展功能,如添加用户认证、实现更复杂的查询等。

edgedb A graph-relational database with declarative schema, built-in migration system, and a next-generation query language edgedb 项目地址: https://gitcode.com/gh_mirrors/ed/edgedb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明俪钧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值