前言

在现代 Web 开发中,Next.js 配合 PostgreSQL 数据库已经成为了构建全栈应用的黄金搭档。而 Drizzle ORM 凭借其“无头”、类型安全且接近原生 SQL 的特性,正迅速成为开发者心中的新宠。

本文将带你从零开始,掌握如何在 Next.js 项目中配置 Drizzle,并熟练进行增删改查操作。


🏗️ 第一步:构建数据库连接单例

在开始写业务代码之前,我们需要建立一个数据库连接。在 Next.js 的开发环境中,由于热更新机制,如果每次请求都新建连接,很容易导致连接数耗尽。因此,我们需要使用全局单例模式来管理 db 实例。

文件路径: src/db/index.ts

import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema'; // 引入你的表结构定义

// 1. 获取连接字符串
const connectionString = process.env.DATABASE_URL!;

// 2. 创建 postgres 客户端
// 注意:在生产环境建议使用连接池配置,开发环境这样写即可
const client = postgres(connectionString);

// 3. 导出 db 实例
// 这个 `db` 对象就是我们在 API 或组件中用来操作数据库的核心工具
export const db = drizzle(client, { schema });

💡 核心提示: 这个 db 对象封装了连接池和 SQL 构建器。只要在任何文件中 import { db } from '@/db',你就拥有了操作数据库的能力。


更推荐

import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './schema';

// 1. 创建数据库连接池
const pool = new Pool({
  connectionString: process.env.DATABASE_URL || 'postgresql://postgres:123456@localhost:5432/my_first_nextjs_db'
});

// 2. 导出 db 实例
// 这个 `db` 对象就是我们在 API 或组件中用来操作数据库的核心工具
export const db = drizzle(pool, { schema });

之前的写法为什么不行

之前的 db/index.ts 使用了 postgres 模块:

import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';

const client = postgres(connectionString);
export const db = drizzle(client, { schema });

失败原因:

  1. 模块兼容性问题postgres 模块(postgresjs/postgres)是一个纯 ESM 模块,在 Next.js 16.2.1 的 Turbopack 环境中存在兼容性问题

  2. Turbopack 的模块解析:Turbopack 对 ESM 模块的处理方式与 Webpack 不同,某些 ESM 模块可能无法正确加载或初始化

  3. 连接初始化时机postgres 模块在导入时就立即尝试建立连接,如果环境变量或模块加载有问题,会导致整个应用启动失败

  4. 错误信息不明显:由于模块加载层面的问题,错误信息往往不明显,难以定位具体问题


现在的写法的优点

安装

npm i pg
npm i @types/pg

现在的 db/index.ts 使用了 pg 模块:

import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';

const pool = new Pool({ connectionString });
export const db = drizzle(pool, { schema });

优点:

  1. 更好的兼容性pg 模块(node-postgres)是一个成熟的 CommonJS 模块,与 Next.js 16.2.1 和 Turbopack 有更好的兼容性

  2. 连接池管理pgPool 提供了内置的连接池管理,可以复用数据库连接,提高性能

  3. 延迟连接Pool 不会立即建立连接,而是在第一次查询时才建立,避免了启动时的连接问题

  4. 更稳定的错误处理pg 模块的错误处理更成熟,更容易捕获和诊断问题

  5. Drizzle ORM 官方支持drizzle-orm/node-postgres 是 Drizzle ORM 官方支持的驱动之一,文档和社区支持更好


总结

特性 postgres 模块 pg 模块
模块类型 ESM CommonJS
Turbopack 兼容性
连接管理 单连接 连接池
稳定性 容易出问题 更稳定
社区支持 较小 更大

所以使用 pg 模块是更可靠的选择,特别是在 Next.js 16.2.1 + Turbopack 的环境中。

📚 第二步:掌握核心增删改查

Drizzle 的语法设计哲学是 “像写 SQL 一样写 TypeScript”。下面我们将通过具体的代码示例,解析最常用的四种操作。

1. 增:插入数据

对应 SQL:INSERT INTO users (name, email) VALUES (...) RETURNING *;

使用 .values() 传入数据对象,配合 .returning() 可以直接拿到插入后的完整数据(包含自增 ID)。

import { db } from '@/db';
import { users } from '@/db/schema';

async function createUser() {
  const newUser = await db.insert(users).values({
    name: "Iron Man",
    email: "ironman@avengers.com"
  }).returning();

  console.log("插入成功:", newUser[0]);
}
2. 删:删除数据

对应 SQL:DELETE FROM users WHERE id = 1;

删除操作必须配合 .where() 使用,否则会导致全表数据被清空(删库跑路警告⚠️)。我们需要引入 eq 函数来构建等值条件。

import { eq } from 'drizzle-orm';

async function deleteUser(userId: number) {
  await db.delete(users).where(eq(users.id, userId));
  console.log("删除成功");
}
3. 改:更新数据

对应 SQL:UPDATE users SET name = 'Captain America' WHERE id = 1;

使用 .set() 指定要修改的字段,同样需要配合 .where() 定位数据。

import { eq } from 'drizzle-orm';

async function updateUser(userId: number) {
  await db.update(users)
    .set({ name: "Captain America" })
    .where(eq(users.id, userId));
    
  console.log("更新成功");
}
4. 查:查询数据

这是最灵活的部分,Drizzle 提供了链式调用来构建复杂的查询语句。

基础查询:

// SELECT * FROM users;
const allUsers = await db.select().from(users);

条件查询:
引入 eq (等于), gt (大于), like (模糊) 等辅助函数。

import { eq, gt, and, like } from 'drizzle-orm';

// SELECT * FROM users WHERE id = 1;
const user = await db.select().from(users).where(eq(users.id, 1));

// 复杂条件:SELECT * FROM users WHERE age > 18 AND name LIKE '%Stark%';
const adults = await db.select()
  .from(users)
  .where(and(gt(users.age, 18), like(users.name, '%Stark%')));

指定字段查询:

// SELECT name, email FROM users;
const names = await db.select({
  name: users.name,
  email: users.email
}).from(users);

🔗 第三步:进阶关联查询

Drizzle 最强大的地方在于类型安全的关联查询。假设你有 users 表和 posts 表,想要查出用户及其文章,可以使用 .leftJoin()

// SELECT * FROM users LEFT JOIN posts ON users.id = posts.userId;
const result = await db.select()
  .from(users)
  .leftJoin(posts, eq(users.id, posts.userId));

// result 的类型会被自动推断为:
// { users: User, posts: Post | null }[]

🛠️ 第四步:Drizzle Kit 命令行工具

除了代码层面的操作,你还需要掌握 Drizzle 的命令行工具来管理数据库结构。

命令 作用 对应 SQL 概念
npx drizzle-kit generate 扫描 schema.ts,生成迁移文件 生成 CREATE TABLE... 等 SQL 脚本
npx drizzle-kit migrate 执行迁移文件,同步到数据库 执行 SQL 脚本
npx drizzle-kit studio 启动可视化界面 类似 pgAdmin 的轻量版

📌 总结

学习 Drizzle 其实就是掌握以下三件事:

  1. 来源:如何从 src/db/index.ts 导出 db 实例。
  2. 语法db.select(), db.insert(), db.update(), db.delete() 配合 .where().values()
  3. 工具:使用 drizzle-kit 同步数据库结构。
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐