数据库搭建
# 04.数据库搭建
数据库采用supabase
登入supabasehttps://supabase.com/
# 注册账号,创建数据库项目
# 需求分析
数据库需要包含的表
- 聊天表格chatsTable
- id用户id使用的大模型
- userId,用户名
- title,标题
- model,使用的大模型
- 消息表格messagesTable
- id,用户id
- chatId,聊天页的编号
- role,对话的角色,分为AI或者用户
- content,聊天的内容
点击注册,可以使用github关联
进入个人Dashboard 仪表板
创建项目
记得将密码保存起来,放在
.env
文件里点击左上角的connect
将链接地址保存在
.env
文件中
# ORM
# 数据库初始化
ORM是指对象关系映射技术(Object Relational Mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。
我们使用 drizzle ORM
找到Get Started
有我们需要的Supabase,打开官方文档 Drizzle ORM - PostgreSQL (opens new window)

Step1.根据官方文档安装依赖
Step2.安装好后,回到我们的
.env
将刚才保存的URL和密码按以下格式改好DATABASE_URL=postgresql://postgres:[PASSWORD]@db.upvkqcmscrcetlwlfimu.supabase.co:5432/postgres
1Step3.在
src/db
中创建index.ts
文件进行连接的初始化,我们需要同步数据,所以复制官方文档的代码 进行修改import { drizzle } from 'drizzle-orm/postgres-js' import postgres from 'postgres' const client = postgres(process.env.DATABASE_URL) const db = drizzle({ client });
1
2
3
4
5Step4.创建数据库表。在db文件夹里创建一个新文件
schema.ts
import { integer, pgTable, varchar } from "drizzle-orm/pg-core"; export const usersTable = pgTable("users", { id: integer().primaryKey().generatedAlwaysAsIdentity(), name: varchar({ length: 255 }).notNull(), age: integer().notNull(), email: varchar({ length: 255 }).notNull().unique(), });
1
2
3
4
5
6
7
8官方帮助我们定义了一个User表,包含用户id、用户名字、年龄、邮箱
我们仿照这个格式定义一个chatsTable(聊天表格),包含用户id,名字,标题,使用的大模型
export const chatsTable = pgTable("chats", { id: serial().primaryKey(), userId:text("user_id").notNull(), title:text("title").notNull(), model:text("model").notNull(), }) //以上代码后续会出现错误 下面是更改之后的 export const chatsTable = pgTable("chats", { id: serial("id").primaryKey(), userId: text("user_id").notNull(), title: text("title").notNull(), model: text("model").notNull() })
1
2
3
4
5
6
7
8
9
10
11
12
13
14并且引入好依赖
import {integer, pgTable, serial, varchar , text} from "drizzle-orm/pg-core";
1定义一个(messagesTable)消息表格,包含用户id、聊天页的编号、对话的角色,分为AI或者用户、聊天的内容
export const messagesTable = pgTable("messages", {
id: serial().primaryKey(),
chatId:text("chat_id").notNull(),
role:text("role").notNull(),
content:text("content").notNull(),
})
//以上代码后续会出现错误 下面是更改之后的
export const messagesTable = pgTable("messages", {
id: serial("id").primaryKey(),
chatId: integer("chat_id").references(() => chatsTable.id),
role: text("role").notNull(),
content: text("content").notNull()
})
2
3
4
5
6
7
8
9
10
11
12
13
14
我们需要再定义两个类型
export type ChatModel =typeof chatsTable.$inferSelect
export type MessageModel =typeof messagesTable.$inferSelect
2
利用 Prisma 的类型推断功能,根据数据库表的查询结果自动定义相应的类型。
Step5.在根目录(和package.json同级)新增一个配置文件
drizzle.config.ts
复制代码,这里记录了schema(概要|描述数据库的组织和结构)的路径、dialect表示输出的方式import 'dotenv/config'; import { defineConfig } from 'drizzle-kit'; export default defineConfig({ out: './drizzle', schema: './src/db/schema.ts', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL!, }, });
1
2
3
4
5
6
7
8
9
10
11Step6.将User表删除,我们用了Clerk定义,后续把自己写的后端接上的时候再用。
Step7.将我们修改过后的代码应用于数据库,在终端执行命令
npx drizzle-kit push
测试连接失败❌ ETIMEDOUT表示连接超时查了一下资料
IPv6 地址的格式是 8组4位十六进制数,用冒号分隔
2406:da18:243:740f:21f6:96ab:3aeb:14fa
而 IPv4 地址是 *4组1-3位十进制数,用点分隔
192.168.1.1
不支持ipv6改用另外两个连接,就是把env文件里的url地址改为这两个
此方法是在项目教学的评论区找到的,建立一个能够交流的社区能够很好的帮助到其他人呀。
连接成功✔
回到supabase项目中 查看连接
发现两个表格的"id"项没有出现预期的关联,原因是建表的语句里少了键值
并且chat_id的属性需要从text转化成integer。由于我们改变了数据类型,
由于我们改变了数据类型, Drizzle-Kit 在迁移过程中无法自动将
chat_id
列从其当前类型,直接回到supabase进行修改,发现还是不行(流汗了) 直接删除表重新创建吧
此时成功
# 定义创建数据库表方法
JS的模块语言
export
关键字:表示这个函数可以被其他模块导入和使用。这是 TypeScript 和 JavaScript 中模块化编程的一部分。const
关键字:声明一个常量,表示这个函数一旦被定义,其引用不能被重新赋值。async
关键字:表示这个函数是一个异步函数。异步函数允许在函数内部使用await
关键字,用于等待异步操作(如数据库操作)完成。await
关键字:用于等待异步操作完成。await
只能在async
函数中使用。箭头函数语法:
=>
表示这是一个箭头函数,箭头函数是 ES6 中引入的一种更简洁的函数写法。箭头函数没有自己的this
上下文,它会捕获其所在上下文的this
值。#
try...catch
块try
块:包含可能抛出错误的代码。如果代码执行过程中发生错误,错误会被捕获并传递到catch
块中。catch
块:捕获try
块中抛出的错误,并进行处理。error
是捕获到的错误对象。
构建创建操作数据库表的函数
chat_id对应多个message,是一对多
# 聊天模块
创建聊天的表格,记录标题,用户id,以及使用的大模型,可以说是内容模块的“身份证“。
插入title,userid,model
// chats
export const createChat = async (title: string, userId: string, model: string) => {
try {
const [newChat] = await db.insert(chatsTable).values({
title,
userId,
model
}).returning()
return newChat
} catch (error) {
console.log("error creating chat", error)
return null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
定义一个get方法,从数据库中获取聊天的数据
export const getChat = async (chatId: number, userId: string) => {
try {
//用and连接查询
const chat = await db.select().from(chatsTable).where(and(eq(chatsTable.id, chatId), eq(chatsTable.userId, userId)))
if (chat.length === 0) {
return null
}
return chat[0]
} catch (error) {
console.log("error getting chat", error)
return null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
侧边栏获取聊天函数
export const getChats = async (userId: string) => {
try {
const chats = await db.select().from(chatsTable).where(eq(chatsTable.userId, userId)).orderBy(desc(chatsTable.id))
return chats
} catch (error) {
console.log("error getting chats", error)
return null
}
}
2
3
4
5
6
7
8
9
10
# 内容模块
按同样的结构,定义查询消息的方法
// messages
export const createMessage = async (chat_id: number, content: string, role: string) => {
try {
const [newMessage] = await db.insert(messagesTable).values({
content: content,
chatId: chat_id,
role: role
}).returning()
return newMessage
} catch (error) {
console.log("error createMessage", error)
return null
}
}
//根据chatid获取消息
export const getMessagesByChatId = async (chatId: number) => {
try {
const messages = await db.select().from(messagesTable).where(eq(messagesTable.chatId, chatId))
return messages
} catch (error) {
console.log("error getMessagesByChatId", error)
return null
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
这样数据库就搭建好了
文字写于:广东