2.1 基本结构
import { pgTable, serial, text, integer, timestamp, boolean } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
id: serial("id").primaryKey(),
email: text("email").notNull().unique(),
name: text("name").notNull(),
age: integer("age"),
isActive: boolean("is_active").default(true),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
第一个参数是数据库中的表名,第二个参数是列的字典——key 是 TS 里的属性名,value 是列定义。列定义的第一个参数是数据库中的列名。
命名约定
TS 里用 camelCase(createdAt),数据库用 snake_case(created_at)——这是 Drizzle 社区的惯例,SQL 里更可读,TS 代码也更自然。
2.2 三大方言的 core 包
import { pgTable } from "drizzle-orm/pg-core"; // Postgres
import { mysqlTable } from "drizzle-orm/mysql-core"; // MySQL
import { sqliteTable } from "drizzle-orm/sqlite-core"; // SQLite
每个方言有自己的列类型(Postgres 有 uuid、jsonb、tsvector;MySQL 有 varchar/decimal;SQLite 类型系统最简单)。本章主要按 Postgres 举例。
2.3 常用列类型(Postgres)
| TS 函数 | Postgres 类型 | TS 读取类型 |
|---|---|---|
| serial | SERIAL(自增 int) | number |
| bigserial | BIGSERIAL | bigint |
| integer / smallint / bigint | INT / SMALLINT / BIGINT | number(bigint 是 string,除非开 mode) |
| real / doublePrecision | REAL / DOUBLE PRECISION | number |
| numeric / decimal | NUMERIC(p, s) | string(精度不丢) |
| text | TEXT | string |
| varchar | VARCHAR(n) | string |
| char | CHAR(n) | string |
| boolean | BOOLEAN | boolean |
| timestamp | TIMESTAMP | Date |
| timestamp({ withTimezone: true }) | TIMESTAMPTZ | Date |
| date / time | DATE / TIME | string |
| interval | INTERVAL | string |
| uuid | UUID | string |
| json / jsonb | JSON / JSONB | unknown(或泛型指定) |
| pgEnum | 自定义枚举 | 字面联合 |
| bytea | BYTEA | Uint8Array |
2.4 默认值
createdAt: timestamp("created_at").defaultNow(), // NOW()
isActive: boolean("is_active").default(true), // 字面值
uuid: uuid("uuid").defaultRandom(), // gen_random_uuid()
config: jsonb("config").$type<{ theme: string }>().default({ theme: "dark" }),
// 任意 SQL 表达式
slug: text("slug").default(sql`gen_random_uuid()::text`),
2.5 枚举(Postgres)
import { pgEnum, pgTable } from "drizzle-orm/pg-core";
export const roleEnum = pgEnum("role", ["admin", "editor", "viewer"]);
export const users = pgTable("users", {
id: serial().primaryKey(),
role: roleEnum("role").notNull().default("viewer"),
});
// role 列的类型被推断为 "admin" | "editor" | "viewer"
2.6 主键与联合主键
// 单列主键(在列上)
id: serial("id").primaryKey(),
// 联合主键(在表的第三参里)
import { primaryKey } from "drizzle-orm/pg-core";
export const userRoles = pgTable("user_roles", {
userId: integer("user_id").notNull(),
roleId: integer("role_id").notNull(),
}, (t) => ({
pk: primaryKey({ columns: [t.userId, t.roleId] }),
}));
2.7 唯一与 unique 约束
// 列级 unique
email: text("email").unique(),
// 复合 unique
import { unique } from "drizzle-orm/pg-core";
export const follows = pgTable("follows", {
followerId: integer("follower_id").notNull(),
followeeId: integer("followee_id").notNull(),
}, (t) => ({
uniq: unique().on(t.followerId, t.followeeId),
}));
2.8 索引
import { index, uniqueIndex } from "drizzle-orm/pg-core";
export const posts = pgTable("posts", {
id: serial().primaryKey(),
title: text().notNull(),
authorId: integer("author_id").notNull(),
publishedAt: timestamp("published_at"),
}, (t) => ({
// 普通索引
authorIdx: index("posts_author_idx").on(t.authorId),
// 复合索引
authorPubIdx: index().on(t.authorId, t.publishedAt.desc()),
// 唯一索引
titleUniq: uniqueIndex().on(t.title),
// 部分索引(Postgres)
published: index().on(t.publishedAt).where(sql`published_at IS NOT NULL`),
}));
2.9 外键
export const posts = pgTable("posts", {
id: serial().primaryKey(),
title: text().notNull(),
// 引用 users.id,删除作者时级联删文章
authorId: integer("author_id")
.notNull()
.references(() => users.id, { onDelete: "cascade", onUpdate: "cascade" }),
});
onDelete/onUpdate 取值:"cascade" / "restrict" / "no action" / "set null" / "set default"。
2.10 JSON / JSONB 列的类型
type Settings = { theme: "dark" | "light"; lang: string };
export const users = pgTable("users", {
id: serial().primaryKey(),
settings: jsonb("settings").$type<Settings>().default({ theme: "dark", lang: "zh" }),
});
// 现在 user.settings.theme 有自动补全
2.11 "Schema 瘦身":用泛型扩展
多表共用某组列(id/createdAt/updatedAt)时提炼函数:
const timestamps = {
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at").defaultNow().notNull().$onUpdate(() => new Date()),
};
export const users = pgTable("users", {
id: serial().primaryKey(),
name: text().notNull(),
...timestamps,
});
2.12 推断 InsertType 与 SelectType
type User = typeof users.$inferSelect; // 读:含 id/createdAt
type NewUser = typeof users.$inferInsert; // 写:可省 id/createdAt
这两个类型跟着 schema 自动变,不用自己维护 DTO。
2.13 小结
- Schema 就是 TypeScript——
pgTable("users", { ... })。 - 列类型 + 修饰符(
.notNull()/.default()/.unique()/.primaryKey()/.references())链式。 - 表级约束(联合主键、复合索引、复合 unique)写在表的第三参 callback 里。
- JSON 列用
.$type<T>()精确化类型。 $inferSelect/$inferInsert自动推出读/写类型,再不用手写 DTO。