跳转至内容
There is a version suitable for your browser's language settings. Would you like to go to the english language of the site?
主页文档

Drizzle

H1

Drizzle 是一个非常易用的 TypeScript ORM 库,可以帮助我们更轻松地和数据库打交道。Milkio 提供了与 Drizzle 结合的方式,只需要安装这个包。

终端窗口
bun i milkio-drizzle

为什么需要使用 Drizzle?

数据库使用 SQL 作为语言,尽管单独使用它很简单,但是,当我们在代码中编写 SQL 时,就会为我们带来困扰。因为 SQL 是字符串,没有语法高亮、自动补全和错误提示,这让我们在编写过程中是痛苦的。同时,我们也很容易写出存在SQL 注入漏洞 的代码。Drizzle 和其他 ORM 库都正在为我们解决这些问题,让我们和数据库打起交道来更加地容易。

入门

在使用 Drizzle 之前,你需要先安装 Drizzle 和它所需要的依赖,根据数据库不同,所需要的依赖也不同。你可以阅读 Drizzle 文档 来了解如何使用你的数据库。下面,是关于使用 MySQL 和 PostgreSQL 的示例。请注意,如果你使用一些 serverless 提供商的数据库服务,即使这些数据库是兼容 MySQL 或 PostgreSQL 的,也请尽量阅读 Drizzle 的文档,这样你可以了解组合他们的最佳方式。

终端窗口
bun i drizzle-orm mysql2 # MySQL
终端窗口
bun i drizzle-orm @vercel/postgres # PostgreSQL

创建数据库表

我们先建立一个 /src/databases 目录,我们会在这个目录中编写数据库表的结构。创建完成后,我们编写一个 user-table.ts 文件,来存储我们的用户数据:

/src/databases/user-table.ts
// 适用于 MySQL
import type typia from "typia";
import { relations, sql } from "drizzle-orm";
import { mysqlTable, int, varchar, datetime } from "drizzle-orm/mysql-core";
export const userTable = mysqlTable(
"user",
{
id: int("id", { unsigned: true }).autoincrement().primaryKey(),
name: varchar("name", { length: 32 }).$type<string & typia.tags.MaxLength<32>>(),
avatar: varchar("avatar", { length: 255 }).$type<string & typia.tags.Format<"url">>(),
email: varchar("email", { length: 320 }).unique().$type<string & typia.tags.Format<"email">>(),
gender: varchar("email", { length: 320 }).unique().$type<"male" | "female" | "other">(),
createdAt: datetime("createdAt")
.notNull()
.default(sql`CURRENT_TIMESTAMP`),
updatedAt: datetime("updatedAt")
.notNull()
.default(sql`CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`),
},
(t) => ({})
);

上面的示例是适用于 MySQL 的代码。如果你使用 PostgreSQL,可以使用下面的代码。

/src/databases/user-table.ts
// 适用于 PostgreSQL
import type typia from "typia";
import { relations, sql } from "drizzle-orm";
import { pgTable, serial, time, varchar } from "drizzle-orm/pg-core";
export const userTable = pgTable(
"user",
{
id: serial("id").primaryKey(),
name: varchar("name", { length: 32 }).$type<string & typia.tags.MaxLength<32>>(),
avatar: varchar("avatar", { length: 255 }).$type<string & typia.tags.Format<"url">>(),
email: varchar("email", { length: 320 }).unique().$type<string & typia.tags.Format<"email">>(),
gender: varchar("email", { length: 320 }).unique().$type<"male" | "female" | "other">(),
createdAt: time("createdAt").notNull(),
updatedAt: time("updatedAt").notNull(),
},
(t) => ({})
);

自动导出数据库表

Drizzle 需要我们将所有的数据库表集合在一起。得益于 Milkio 的 生成阶段,我们可以将数据库表分开建立的同时,又不用手动编写一个文件将所有的数据库表结合在一起。我们在 /milkio.tomlgenerate 配置中,在 significant 中添加下面的命令,这样在我们每次修改完文件后,Milkio 就会自动读取 /src/databases 目录,并将所有的 .ts 文件都在 /generated/database-schema.ts 中合并。

/milkio.toml
[generate]
significant = ['./node_modules/milkio-drizzle/g.ts']

数据库配置

接下来,我们编写数据库的连接信息。我们在 /src/config 目录下,创建一个 drizzle.ts 文件,并将数据库的连接信息填写在里面。如果你不知道这些信息该填写为什么,请询问你所使用的数据库提供商。

import { env } from "node:process";
import { envToBoolean, envToNumber, envToString } from "milkio";
export const configDrizzle = {
databaseUrl: envToString(env.DATABASE_URL, "your-default-database-url"),
dbHost: envToString(env.DB_HOST, "your-default-database-host"),
dbPort: envToNumber(env.DB_PORT, 3306),
dbUsername: envToString(env.DB_USERNAME, "your-default-username"),
dbPassword: envToString(env.DB_PASSWORD, "your-default-password"),
dbDatabase: envToString(env.DB_DATABASE, "your-default-database"),
};

在你的 /src/uses 目录中,创建一个 drizzle.ts,并将下面的代码粘贴进去。下面的示例代码是 MySQL 的,你可能需要阅读 Drizzle Get Started,根据你实际所使用的数据库或者在使用的 serverless 提供商来修改下面的代码。总而言之,就是将 Drizzle 放到 Milkio 的 Use 里。

/src/uses/drizzle.ts
// 适用于 MySQL
import { defineUse } from "milkio";
import * as schema from "../../generated/database-schema";
import { configDrizzle } from "../config/drizzle";
import { drizzle } from "drizzle-orm/mysql2";
import mysql from "mysql2/promise";
export const useDrizzle = defineUse(async () => {
const connection = await mysql.createConnection({
host: configDrizzle.dbHost,
port: configDrizzle.dbPort,
user: configDrizzle.dbUsername,
password: configDrizzle.dbPassword,
database: configDrizzle.dbDatabase,
});
return drizzle(connection, { schema, mode: "default" });
});

上面的示例是适用于 MySQL 的代码。如果你使用 PostgreSQL,可以使用下面的代码。

/src/uses/drizzle.ts
// 适用于 PostgreSQL
// Todo..

编写 API

假设编写一个创建用户的 API,得益于 Drizzle 完善的类型系统,我们可以很轻易地限制 API 的 params 和数据库表结构一致。

import { defineApi } from "milkio";
import { DBInsert } from "milkio-drizzle";
import { userTable } from "../generated/database-schema";
export const api = defineApi({
meta: {},
async action(
params: {
user: DBInsert<typeof userTable>;
},
context
) {
const drizzle = await useDrizzle();
await drizzle.insert(userTable).values(params.user);
},
});

更多类型

Milkio 提供了一些辅助类型,帮助你在 API 中更好地对 params 做限制。

DBInsert

插入数据的类型,最常用,和你数据库表所定义的类型相同。

DBInsert<typeof userTable>;

DBSelect

取出数据的类型,和你数据库表所定义的类型相同。

DBSelect<typeof userTable>;

DBPartial

将数据库表中所有的字段都变为可选的,通常用于允许增量提交的更新 API 中。

DBOmit

删除数据库表中的字段,通常用于防止部分字段被更新,如 idcreatedAt 等,以及避免和权限相关的字段被更新,例如 isAdmin

DBOmit<typeof userTable, "id" | "createdAt" | "updatedAt" | "deletedAt">;

下一步?

阅读 Drizzle 的文档吧!这样你就可以了解它的详细用法了。