5.1 为什么选 Drizzle
Turso/libSQL 的 ORM 选择上 Drizzle 优势明显:
- 原生 SQLite dialect——drizzle-orm/sqlite-core 完全匹配 SQLite 语法
- drizzle-kit 支持 turso driver——迁移直接推到远端
- 零运行时依赖——跑 Cloudflare Workers/Edge 毫无问题
- Drizzle 官方与 Turso 深度合作——文档案例多
Prisma 也支持 libSQL,但需要 preview feature flag + 代码生成器;中小项目用 Drizzle 更顺手。
5.2 安装
pnpm add drizzle-orm @libsql/client
pnpm add -D drizzle-kit
5.3 定义 schema
// db/schema.ts
import { sqliteTable, integer, text, real, blob } from 'drizzle-orm/sqlite-core';
import { relations, sql } from 'drizzle-orm';
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
email: text('email').notNull().unique(),
name: text('name').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.notNull()
.default(sql`(unixepoch())`),
});
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
userId: integer('user_id').notNull().references(() => users.id),
title: text('title').notNull(),
body: text('body'),
published: integer('published', { mode: 'boolean' }).default(false),
});
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts),
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.userId], references: [users.id] }),
}));
关键列类型
| Drizzle 列 | SQLite 存储 | JS 类型 |
|---|---|---|
| integer(), autoIncrement | INTEGER | number |
| integer(x, { mode: 'boolean' }) | INTEGER 0/1 | boolean |
| integer(x, { mode: 'timestamp' }) | INTEGER(秒) | Date |
| text() | TEXT | string |
| text({ mode: 'json' }) | TEXT(JSON) | 自定义类型 |
| real() | REAL | number |
| blob() | BLOB | Uint8Array |
5.4 drizzle.config.ts
import { defineConfig } from 'drizzle-kit';
export default defineConfig({
schema: './db/schema.ts',
out: './db/migrations',
dialect: 'turso', // 或 'sqlite'
dbCredentials: {
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
},
});
dialect: 'turso' 会让 drizzle-kit 用 libSQL HTTP 驱动;本地开发纯 SQLite 文件就用 'sqlite'。
5.5 生成和应用迁移
pnpm drizzle-kit generate # 对比 schema vs 库,生成 SQL 到 db/migrations/
pnpm drizzle-kit migrate # 把迁移 apply 到远端
# 原型阶段可以跳过 migration 文件,直接 push
pnpm drizzle-kit push
push 适合开发期;生产应该走 generate + migrate——版本可审计。
5.6 client 初始化
// db/index.ts
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client';
import * as schema from './schema';
const client = createClient({
url: process.env.TURSO_DATABASE_URL!,
authToken: process.env.TURSO_AUTH_TOKEN!,
});
export const db = drizzle(client, { schema });
5.7 基础 CRUD
import { db } from './db';
import { users, posts } from './db/schema';
import { eq, desc } from 'drizzle-orm';
// Insert,返回插入结果
const [alice] = await db.insert(users).values({
email: 'alice@example.com',
name: 'Alice',
}).returning();
// Select
const all = await db.select().from(users).orderBy(desc(users.createdAt));
// Update
await db.update(users).set({ name: 'Alicia' }).where(eq(users.id, alice.id));
// Delete
await db.delete(posts).where(eq(posts.userId, alice.id));
5.8 关系查询(Relational Queries)
// 取用户 + 他们的已发布博文 + 每篇博文的标题
const result = await db.query.users.findMany({
with: {
posts: {
where: (posts, { eq }) => eq(posts.published, true),
columns: { id: true, title: true },
orderBy: (posts, { desc }) => desc(posts.id),
},
},
});
Drizzle 把它编译成一条 SQL,而不是 N+1 查询——libSQL 执行一次就返回嵌套结构。
5.9 部署 Cloudflare Workers
// src/index.ts
import { Hono } from 'hono';
import { drizzle } from 'drizzle-orm/libsql';
import { createClient } from '@libsql/client/web';
import * as schema from './schema';
type Env = { Bindings: { TURSO_URL: string; TURSO_TOKEN: string } };
const app = new Hono<Env>();
app.get('/posts', async (c) => {
const client = createClient({
url: c.env.TURSO_URL, authToken: c.env.TURSO_TOKEN,
});
const db = drizzle(client, { schema });
const posts = await db.query.posts.findMany({
where: (p, { eq }) => eq(p.published, true),
limit: 20,
});
return c.json(posts);
});
export default app;
wrangler.toml 放 TURSO_URL / TURSO_TOKEN(secret),部署 wrangler deploy。
5.10 嵌入式副本 + Drizzle(Node)
const client = createClient({
url: 'file:./data/app.db',
syncUrl: process.env.TURSO_URL!,
authToken: process.env.TURSO_TOKEN!,
syncInterval: 30,
});
await client.sync();
export const db = drizzle(client, { schema });
Drizzle 层完全无感——你换 syncUrl 方式底层客户端即可,上层 query 代码不变。
5.11 类型推断
// 从 schema 直接推插入/查询类型
import { type InferInsertModel, type InferSelectModel } from 'drizzle-orm';
type NewUser = InferInsertModel<typeof users>;
type User = InferSelectModel<typeof users>;
小结
Drizzle + libSQL = TypeScript 优先的 Turso 访问层。schema 在 TS、drizzle-kit 管迁移、query 返回值自动推断。部署时 Edge 用 /web driver、Node 可选嵌入式副本,上层代码一模一样。下一章深入事务与一致性。