Vafast + Drizzle:轻量高效的全栈类型安全方案

Drizzle ORM 是近年来备受关注的 TypeScript ORM,以其轻量、高性能和类 SQL 语法而著称。
与 Prisma 不同,Drizzle 没有代码生成步骤,类型直接从 schema 定义中推断,这使得它与 Vafast 的类型系统完美契合。
为什么选择 Drizzle
轻量级:Drizzle 的包体积非常小,没有复杂的运行时依赖。
类 SQL 语法:如果你熟悉 SQL,Drizzle 的 API 会让你感到非常自然。
零代码生成:不需要额外的 generate 步骤,类型直接可用。
边缘运行时友好:Drizzle 可以在 Cloudflare Workers、Vercel Edge 等边缘环境中运行。
快速开始
首先创建一个 Vafast 项目:
npx create-vafast-app vafast-drizzle
cd vafast-drizzle安装 Drizzle 相关依赖:
npm add drizzle-orm better-sqlite3
npm add -D drizzle-kit @types/better-sqlite3这里我们使用 SQLite 作为演示,Drizzle 同样支持 PostgreSQL、MySQL 等数据库。
定义 Schema
创建 src/db/schema.ts:
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core'
export const users = sqliteTable('users', {
id: integer('id').primaryKey({ autoIncrement: true }),
username: text('username').notNull().unique(),
email: text('email').notNull(),
createdAt: integer('created_at', { mode: 'timestamp' })
.$defaultFn(() => new Date())
})
export const posts = sqliteTable('posts', {
id: integer('id').primaryKey({ autoIncrement: true }),
title: text('title').notNull(),
content: text('content').notNull(),
authorId: integer('author_id')
.notNull()
.references(() => users.id),
createdAt: integer('created_at', { mode: 'timestamp' })
.$defaultFn(() => new Date())
})创建 src/db/index.ts:
import Database from 'better-sqlite3'
import { drizzle } from 'drizzle-orm/better-sqlite3'
import * as schema from './schema'
const sqlite = new Database('sqlite.db')
export const db = drizzle(sqlite, { schema })配置 Drizzle Kit
创建 drizzle.config.ts:
import { defineConfig } from 'drizzle-kit'
export default defineConfig({
schema: './src/db/schema.ts',
out: './drizzle',
dialect: 'sqlite',
dbCredentials: {
url: 'sqlite.db'
}
})生成并推送数据库:
npx drizzle-kit push创建 API
现在让我们用 Vafast 创建 CRUD API:
import { Server, defineRoute, defineRoutes, serve, Type, err } from 'vafast'
import { db } from './db'
import { users, posts } from './db/schema'
import { eq } from 'drizzle-orm'
// 定义 Schema
const CreateUserBody = Type.Object({
username: Type.String({ minLength: 3 }),
email: Type.String({ format: 'email' })
})
const CreatePostBody = Type.Object({
title: Type.String({ minLength: 1 }),
content: Type.String(),
authorId: Type.Number()
})
const routes = defineRoutes([
// 用户相关
defineRoute({
method: 'GET',
path: '/users',
handler: async () => {
return await db.select().from(users)
}
}),
defineRoute({
method: 'GET',
path: '/users/:id',
schema: { params: Type.Object({ id: Type.String() }) },
handler: async ({ params }) => {
const user = await db
.select()
.from(users)
.where(eq(users.id, Number(params.id)))
.get()
if (!user) {
throw err.notFound('用户不存在')
}
return user
}
}),
defineRoute({
method: 'POST',
path: '/users',
schema: { body: CreateUserBody },
handler: async ({ body }) => {
const result = await db.insert(users).values(body).returning()
return result[0]
}
}),
// 文章相关
defineRoute({
method: 'GET',
path: '/posts',
handler: async () => {
return await db.select().from(posts)
}
}),
defineRoute({
method: 'POST',
path: '/posts',
schema: { body: CreatePostBody },
handler: async ({ body }) => {
const result = await db.insert(posts).values(body).returning()
return result[0]
}
}),
// 获取用户的所有文章
defineRoute({
method: 'GET',
path: '/users/:id/posts',
schema: { params: Type.Object({ id: Type.String() }) },
handler: async ({ params }) => {
return await db
.select()
.from(posts)
.where(eq(posts.authorId, Number(params.id)))
}
})
])
const server = new Server(routes)
serve({ fetch: server.fetch, port: 3000 }, () => {
console.log('服务器运行在 http://localhost:3000')
})关联查询
Drizzle 支持强大的关联查询。让我们添加一个获取文章详情(包含作者信息)的接口:
import { db } from './db'
import { users, posts } from './db/schema'
import { eq } from 'drizzle-orm'
// 获取文章及作者信息
defineRoute({
method: 'GET',
path: '/posts/:id/detail',
schema: { params: Type.Object({ id: Type.String() }) },
handler: async ({ params }) => {
const result = await db
.select({
post: posts,
author: {
id: users.id,
username: users.username
}
})
.from(posts)
.leftJoin(users, eq(posts.authorId, users.id))
.where(eq(posts.id, Number(params.id)))
.get()
if (!result) {
throw err.notFound('文章不存在')
}
return {
...result.post,
author: result.author
}
}
})事务处理
Drizzle 支持事务,确保数据一致性:
const TransferBody = Type.Object({
fromUserId: Type.Number(),
toUserId: Type.Number(),
postId: Type.Number()
})
defineRoute({
method: 'POST',
path: '/posts/transfer',
schema: { body: TransferBody },
handler: async ({ body }) => {
return await db.transaction(async (tx) => {
// 检查文章是否属于原作者
const post = await tx
.select()
.from(posts)
.where(eq(posts.id, body.postId))
.get()
if (!post || post.authorId !== body.fromUserId) {
throw err.badRequest('无权转让此文章')
}
// 更新文章作者
const result = await tx
.update(posts)
.set({ authorId: body.toUserId })
.where(eq(posts.id, body.postId))
.returning()
return result[0]
})
}
})边缘部署
Drizzle 的一大优势是支持边缘运行时。使用 D1(Cloudflare 的 SQLite):
// 在 Cloudflare Workers 中使用
import { drizzle } from 'drizzle-orm/d1'
import * as schema from './db/schema'
export default {
async fetch(request: Request, env: Env) {
const db = drizzle(env.DB, { schema })
const server = new Server(routes)
return server.fetch(request)
}
}Vafast + Drizzle 的优势
类型一致性:Drizzle 的 schema 类型与 Vafast 的 Type schema 可以完美配合,实现从数据库到 API 的完整类型安全。
零生成步骤:不需要运行 prisma generate,修改 schema 后类型立即可用。
边缘友好:两者都支持各种边缘运行时,可以轻松部署到 Cloudflare Workers、Vercel Edge 等平台。
高性能:Vafast 的高性能路由 + Drizzle 的轻量查询,带来极致的性能体验。
总结
Drizzle ORM 与 Vafast 的组合为 TypeScript 开发者提供了一套轻量、高效、类型安全的全栈解决方案。
如果你追求更小的包体积、更快的启动速度、更接近 SQL 的开发体验,Drizzle + Vafast 是一个值得尝试的选择。
查看更多: