数据库集成
ubean 的数据库层(defineDatabase / useDatabase)构建在 db0 的 connector 抽象之上,并自带内存回退实现。若需要更丰富的查询 API,可以引入自己的 ORM,并通过虚拟模块接线,让各个 handler 导入同一个客户端实例。
内置 API 的完整参考见 数据库操作。
Drizzle ORM
安装
pnpm add drizzle-orm
pnpm add -D drizzle-kit
# 再加上你选用的驱动:
pnpm add better-sqlite3 # SQLite (Node)
# 或
pnpm add postgres # PostgreSQL
# 或
pnpm add mysql2 # MySQL方案 A —— 通过虚拟模块注册(推荐)
// ubean.config.ts
import { defineConfig } from 'ubean';
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
const client = new Database('./data/app.sqlite');
const db = drizzle(client);
export default defineConfig({
modules: [
{
name: 'drizzle',
setup(_options, kit) {
kit.addVirtualImports({
'#db': () => ({ default: db, db })
});
}
}
]
});// src/db/schema.ts
import { sqliteTable, integer, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users', {
id: integer('id').primaryKey(),
name: text('name').notNull(),
email: text('email').unique().notNull()
});// routes/api/users.ts
import { defineHandler } from 'ubean/server';
import db, { users } from '#db';
export const GET = defineHandler(async c => {
const rows = await db.select().from(users).limit(50);
return c.json(rows);
});
export const POST = defineHandler(async c => {
const body = await c.req.json();
const [created] = await db.insert(users).values(body).returning();
return c.json(created, 201);
});迁移
Drizzle 的迁移由 drizzle-kit 管理:
{
"scripts": {
"db:generate": "drizzle-kit generate",
"db:migrate": "drizzle-kit migrate",
"db:studio": "drizzle-kit studio"
}
}如果想在 ubean 启动时以编程方式执行迁移,可以把 drizzle-kit generate 产出的 SQL 交给 migrateDatabase(db, statements)。
Prisma
安装
pnpm add @prisma/client
pnpm add -D prismaSchema 与客户端
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
name String
email String @unique
}npx prisma generate
npx prisma migrate dev --name init通过虚拟模块接线
// ubean.config.ts
import { defineConfig } from 'ubean';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default defineConfig({
modules: [
{
name: 'prisma',
setup(_options, kit) {
kit.addVirtualImports({
'#prisma': () => ({ default: prisma, prisma })
});
}
}
]
});// routes/api/users.ts
import { defineHandler } from 'ubean/server';
import prisma from '#prisma';
export const GET = defineHandler(async c => {
const users = await prisma.user.findMany();
return c.json(users);
});使用内置数据库层
对于用 ORM 显得过重的简单场景,直接使用 defineDatabase + useDatabase:
// app.ts (或某个模块的 setup)
import { defineDatabase, registerDb0Create } from 'ubean/server';
import { postgres } from 'db0/connectors/postgres';
// 注册一次 db0 工厂
registerDb0Create(createFn);
export const db = defineDatabase({
connector: postgres({ url: process.env.DATABASE_URL! })
});// routes/api/users.ts
import { defineHandler, useDatabase } from 'ubean/server';
export const GET = defineHandler(async c => {
const db = useDatabase();
const { rows } = await db.sql<{ id: number; email: string }>`
SELECT id, email FROM users ORDER BY id DESC LIMIT 50
`;
return c.json(rows);
});MongoDB(Mongoose)
pnpm add mongoose// ubean.config.ts
import { defineConfig } from 'ubean';
import mongoose from 'mongoose';
export default defineConfig({
modules: [
{
name: 'mongoose',
async setup(_options, kit) {
await mongoose.connect(process.env.MONGODB_URI!);
kit.addVirtualImports({
'#mongoose': () => ({ default: mongoose, mongoose })
});
}
}
]
});// src/db/models/User.ts
import { Schema, model } from '#mongoose';
const userSchema = new Schema({
name: String,
email: { type: String, unique: true }
});
export const User = model('User', userSchema);// routes/api/users.ts
import { defineHandler } from 'ubean/server';
import { User } from '@/db/models/User';
export const GET = defineHandler(async c => {
const users = await User.find();
return c.json(users);
});最佳实践
- schema 稍复杂就优先用 ORM 而非裸 SQL —— 类型安全带来的收益很快就显现出来。
- 客户端只注册一次 —— 在
setup()里创建实例,再通过addVirtualImports暴露;绝不要按请求实例化。 - 使用迁移 —— Drizzle 与 Prisma 都自带迁移工具,用 npm scripts 串起来即可。
- 凭据放进
defineEnv(),用{ type: String, required: true }声明 —— 也绝不要把含真实密钥的.env文件提交到仓库。 - 关闭时释放连接 —— 在 shutdown hook 中调用
closeDatabases()(内置数据库层),或await mongoose.disconnect()/await prisma.$disconnect()。