← 中文资源

简体中文版本

Drizzle PostgreSQL 类型安全联表

用 Drizzle 查询构建器对 PostgreSQL 做类型安全联表:select 形状决定结果类型、条件全部参数化、推断关系零手写映射。

阅读英文原文

示例说明

Drizzle 的表定义是唯一事实源:列类型即 TypeScript 类型,select 里挑了哪些列结果就长什么样,漏列多列都过不了编译。联表条件用 eq 这类运算符函数表达,值走参数化通道而非字符串拼接。相比手写 SQL,重构表结构时所有受影响查询会被编译器逐个点名。

技术要点

  • select 形状直接决定行的类型
  • 运算符函数保证条件参数化
  • 表定义与数据库结构的同步迁移

代码解读

recentBigOrders 的 select 挑选三个列,返回数组的元素类型即由此推断;innerJoin 用 eq 匹配外键关系,where 组装大于等于条件,orderBy 与 limit 完成排序截断。查询结束后 pool.end 归还连接资源。

使用建议

列名映射让数据库 snake_case 与代码 camelCase 自然转换。复杂统计用 sql 模板嵌入原生片段但保持参数化。迁移文件要进版本库并评审。

代码示例

import { pgTable, serial, text, integer, timestamp } from 'drizzle-orm/pg-core';
import { drizzle } from 'drizzle-orm/node-postgres';
import { eq, gte, desc, and, sql } from 'drizzle-orm';
import { Pool } from 'pg';

const customers = pgTable('customers', {
  id: serial('id').primaryKey(),
  email: text('email').notNull(),
});

const orders = pgTable('orders', {
  id: serial('id').primaryKey(),
  customerId: integer('customer_id').notNull(),
  total: integer('total').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
});

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = drizzle(pool);

async function recentBigOrders(minTotal: number) {
  const rows = await db
    .select({
      orderId: orders.id,
      email: customers.email,
      total: orders.total,
    })
    .from(orders)
    .innerJoin(customers, eq(orders.customerId, customers.id))
    .where(and(gte(orders.total, minTotal)))
    .orderBy(desc(orders.createdAt))
    .limit(50);

  return rows;
}

const qualified = await recentBigOrders(1000);
console.log('rows typed:', qualified[0]?.email.length ?? 0);
await pool.end();