@gulibs/tegg-prisma
v0.1.6
Published
Prisma plugin for Egg.js 4.x
Maintainers
Readme
@gulibs/tegg-prisma
适用于 Egg.js 4.x 的 Prisma 插件,提供完整的 TypeScript 支持。
系统要求
- Node.js >= 22.18.0
- Egg.js >= 4.1.0-beta.35
- Prisma >= 6.0.0(完全支持 Prisma 7.x)
特性
- ✅ 自动类型推导 - 零配置 TypeScript 支持!(v0.1.3+)
- ✅ 完整的 Prisma schema IntelliSense
- ✅ Driver Adapter 支持 - Prisma 7.x(v0.1.3+)
- ✅ 单数据库和多数据库连接
- ✅ 自动连接和优雅断开
- ✅ 支持自定义 Prisma Client
- ✅ Agent 进程支持(可选)
- ✅ 通过
app.teggPrisma和ctx.prisma便捷访问 - ✅ 查询性能监控
- ✅ 健康检查 API
- ✅ 带自动重试的事务
- ✅ 增强的错误处理
安装
npm i @gulibs/tegg-prisma @prisma/client
npm i -D prisma快速开始
1. 启用插件
// config/plugin.ts
import prismaPlugin from '@gulibs/tegg-prisma';
export default {
...prismaPlugin(),
};2. 设置 Prisma
# 初始化 Prisma
npx prisma init
# 在 prisma/schema.prisma 中定义模型
# 然后生成 Prisma Client
npx prisma generate3. 启动应用
npm run dev就这样! TypeScript 会自动推导 PrismaClient 类型 - 无需任何配置。
4. 配置
// config/config.default.ts
export default {
teggPrisma: {
client: {
// 可选:覆盖数据库 URL
// DATABASE_URL 从 .env 或 schema.prisma 中配置
// 可选:配置日志
log: ['query', 'error', 'warn'],
// 可选:错误格式
errorFormat: 'colorless',
}
}
};5. 使用
// app/controller/user.ts
import { Controller, HTTPController, HTTPMethod, HTTPContext } from 'egg';
@HTTPController()
export class UserController {
@HTTPMethod({
path: '/users',
method: 'GET',
})
async list(@HTTPContext() ctx: Context) {
// 通过 ctx.prisma 访问 Prisma Client
const users = await ctx.prisma.user.findMany();
ctx.body = users;
}
@HTTPMethod({
path: '/users/:id',
method: 'GET',
})
async show(@HTTPContext() ctx: Context) {
const { id } = ctx.params;
const user = await ctx.prisma.user.findUnique({
where: { id },
});
ctx.body = user;
}
@HTTPMethod({
path: '/users',
method: 'POST',
})
async create(@HTTPContext() ctx: Context) {
const user = await ctx.prisma.user.create({
data: ctx.request.body,
});
ctx.body = user;
}
}类型安全 - 自动类型推导 🎯
v0.1.3 新特性:零配置类型推导!
插件现在自动推导 PrismaClient 类型 - 无需手动类型断言或配置!
// ✅ 直接使用 - IntelliSense 自动生效!
const users = await ctx.prisma.user.findMany({
include: { posts: true } // ✅ 自动补全生效!
});
const user = await ctx.prisma.user.create({
data: {
name: 'John',
email: '[email protected]',
posts: {
create: { title: '我的第一篇文章' }
}
}
});工作原理:
- 插件使用 TypeScript 条件类型自动从
@prisma/client导入和推导PrismaClient类型 - 当你运行
npx prisma generate时,schema 模型会被完全类型化 - 插件的
EggPrisma类型自动解析为你生成的PrismaClient实例类型 - 零配置 - 只需安装和使用!
结果:在项目的任何地方都有完美的 IntelliSense!
数据库连接方式
Prisma 7.x 支持两种数据库连接配置方式:
方式 1:环境变量(推荐用于大多数情况)
适用于:单数据库,简单配置
在 .env 文件中配置 DATABASE_URL:
# .env
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb?connection_limit=10&pool_timeout=30"// config/config.default.ts
export default {
teggPrisma: {
client: {
// DATABASE_URL 自动从环境变量读取
log: ['query', 'error', 'warn'],
}
}
};优点:简单,符合 12-factor app 原则,兼容 Prisma CLI
方式 2:Driver Adapter(Prisma 7.x 新增)
适用于:动态数据库配置,多数据库,运行时连接变更
安装 adapter:
# PostgreSQL
npm i @prisma/adapter-pg pg
# MySQL
npm i @prisma/adapter-mysql2 mysql2
# 其他 adapter:@prisma/adapter-planetscale, @prisma/adapter-neon 等在配置中使用 adapter:
// config/config.default.ts
import { PrismaPg } from '@prisma/adapter-pg';
// 创建带有 connectionString 的 adapter
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL
});
export default {
teggPrisma: {
client: {
adapter, // 将 adapter 传递给插件
log: ['query', 'error', 'warn'],
}
}
};优点:
- ✅ 运行时配置(可以动态更改连接)
- ✅ 支持边缘运行时(Cloudflare Workers、Vercel Edge 等)
- ✅ 高级连接池控制
- ✅ 多数据库支持不同驱动
配置
单数据库
export default {
teggPrisma: {
client: {
// DATABASE_URL 从 .env 或 schema.prisma 中配置
log: ['query', 'error', 'warn'],
// 可选:事务选项
transactionOptions: {
maxWait: 5000,
timeout: 10000,
isolationLevel: 'ReadCommitted',
},
// 可选:查询监控
queryMonitor: {
enabled: true,
slowQueryThreshold: 1000, // 1秒
logAllQueries: process.env.NODE_ENV === 'development',
},
}
}
};多数据库
export default {
teggPrisma: {
clients: {
db1: {
// 使用自定义 PrismaClient(从 schema-db1.prisma 生成)
log: ['error'],
},
db2: {
// 使用自定义 PrismaClient(从 schema-db2.prisma 生成)
log: ['error'],
},
}
}
};访问多个数据库:
// 获取特定客户端
const db1 = app.teggPrisma.get('db1');
const users = await db1.user.findMany();
// 或通过 ctx
const db2 = ctx.prisma.get('db2');
const posts = await db2.post.findMany();自定义 Prisma Client
如果有自定义生成的 Prisma Client(例如在 monorepo 中):
import { PrismaClient as CustomPrismaClient } from './prisma/client';
export default {
teggPrisma: {
client: {
PrismaClient: CustomPrismaClient,
// DATABASE_URL 从 .env 或 schema.prisma 中配置
}
}
};Agent 支持
在 agent 进程中启用 Prisma(用于定时任务等):
export default {
teggPrisma: {
agent: true, // 在 agent 中启用
client: {
// DATABASE_URL 从 .env 或 schema.prisma 中配置
}
}
};API
app.teggPrisma
在应用中访问 Prisma Client:
// 单客户端
const users = await app.teggPrisma.user.findMany();
// 多客户端
const db1 = app.teggPrisma.get('db1');
const users = await db1.user.findMany();
// 或使用别名
const db2 = app.teggPrismas.get('db2');ctx.prisma
在上下文中访问 Prisma Client:
async show(ctx: Context) {
const user = await ctx.prisma.user.findUnique({
where: { id: ctx.params.id },
});
ctx.body = user;
}CLIENT_NAME_SYMBOL
获取客户端名称:
import { CLIENT_NAME_SYMBOL } from '@gulibs/tegg-prisma';
const clientName = Reflect.get(ctx.prisma, CLIENT_NAME_SYMBOL);
console.log(clientName); // 'default' 或客户端名称健康检查 API
检查数据库连接健康状态:
// 单客户端
const healthy = await app.checkPrismaHealth();
if (!healthy) {
console.error('数据库连接不健康');
}
// 多客户端
const db1Healthy = await app.checkPrismaHealth('db1');
const db2Healthy = await app.checkPrismaHealth('db2');
// 获取所有客户端健康状态
const status = await app.getPrismaHealthStatus();
console.log(status); // { default: true, db1: true, db2: false }健康检查端点示例:
@HTTPMethod({ path: '/health', method: 'GET' })
async healthCheck(@HTTPContext() ctx: Context) {
const dbStatus = await ctx.app.getPrismaHealthStatus();
const allHealthy = Object.values(dbStatus).every(v => v);
ctx.body = {
status: allHealthy ? 'ok' : 'error',
database: dbStatus,
timestamp: new Date().toISOString(),
};
ctx.status = allHealthy ? 200 : 503;
}高级功能
1. 查询性能监控
使用 Prisma Client Extensions 监控慢查询并追踪查询性能。
要求:Prisma Client 4.16.0 或更高版本
工作原理:插件使用 Prisma 的 $extends API 拦截和监控所有数据库查询,不会影响性能。
// config/config.default.ts
export default {
teggPrisma: {
client: {
// DATABASE_URL 从 .env 或 schema.prisma 中配置
queryMonitor: {
enabled: true,
slowQueryThreshold: 1000, // 查询超过1秒发出警告
logAllQueries: process.env.NODE_ENV === 'development',
onSlowQuery: (info) => {
// 发送到监控服务
console.warn('检测到慢查询:', {
model: info.model,
action: info.action,
duration: info.duration,
timestamp: info.timestamp,
});
},
onQuery: (info) => {
// 在开发环境追踪所有查询
console.log(`查询: ${info.model}.${info.action} - ${info.duration}ms`);
},
},
}
}
};注意:查询监控使用 Prisma 4.16.0 引入的 Client Extensions ($extends),它替代了已废弃的 $use middleware API。
2. 连接池配置
重要:在 Prisma 7.x 中,连接池设置必须在 DATABASE_URL 连接字符串中配置:
# .env - 在连接字符串中配置池设置
DATABASE_URL="postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=30"export default {
teggPrisma: {
client: {
// DATABASE_URL 从 .env 或 schema.prisma 中配置 // 池设置从 URL 读取
transactionOptions: {
maxWait: 5000, // 事务开始的最大等待时间
timeout: 10000, // 事务执行的最大时间
isolationLevel: 'ReadCommitted',
},
}
}
};常用连接池参数:
connection_limit- 最大连接数(PostgreSQL/MySQL)pool_timeout- 超时时间(秒)connect_timeout- 连接超时
参考:https://www.prisma.io/docs/guides/database/connection-pooling
3. 带自动重试的事务
使用事务辅助函数,自动重试逻辑:
import { withTransaction } from '@gulibs/tegg-prisma';
async transfer(@HTTPContext() ctx: Context) {
const result = await withTransaction(
ctx.prisma,
async (tx) => {
const from = await tx.account.update({
where: { id: ctx.request.body.fromId },
data: { balance: { decrement: ctx.request.body.amount } },
});
if (from.balance < 0) {
throw new Error('余额不足');
}
const to = await tx.account.update({
where: { id: ctx.request.body.toId },
data: { balance: { increment: ctx.request.body.amount } },
});
return { from, to };
},
{
maxRetries: 3,
timeout: 10000,
isolationLevel: 'Serializable',
}
);
ctx.body = result;
}4. 增强的错误处理
使用内置的错误处理工具:
import { isPrismaError, getPrismaErrorMessage, PrismaErrorCodes } from '@gulibs/tegg-prisma';
async create(@HTTPContext() ctx: Context) {
try {
const user = await ctx.prisma.user.create({
data: ctx.request.body,
});
ctx.body = user;
} catch (error) {
if (isPrismaError(error)) {
if (error.code === PrismaErrorCodes.UNIQUE_CONSTRAINT) {
ctx.status = 400;
ctx.body = {
error: '邮箱已存在',
message: getPrismaErrorMessage(error),
};
return;
}
if (error.code === PrismaErrorCodes.FOREIGN_KEY_CONSTRAINT) {
ctx.status = 400;
ctx.body = { error: '引用的记录不存在' };
return;
}
}
throw error;
}
}5. 原生 SQL 查询
需要时执行原生 SQL:
// 使用 app
const users = await app.prismaRawQuery`
SELECT * FROM "User" WHERE age > ${18}
`;
// 使用 ctx
const stats = await ctx.prisma.$queryRaw`
SELECT COUNT(*) as total, AVG(age) as avgAge FROM "User"
`;6. Prisma 指标
获取 Prisma Client 指标(如果启用了指标):
const metrics = await app.getPrismaMetrics();
if (metrics) {
console.log('活跃连接数:', metrics.connections?.active);
console.log('查询持续时间:', metrics.queries?.duration);
}最佳实践
1. 事务支持
async transfer(ctx: Context) {
const result = await ctx.prisma.$transaction(async (prisma) => {
await prisma.account.update({
where: { id: 1 },
data: { balance: { decrement: 100 } },
});
await prisma.account.update({
where: { id: 2 },
data: { balance: { increment: 100 } },
});
return { success: true };
});
ctx.body = result;
}2. 查询优化
// ✅ 使用 select 获取特定字段
const users = await ctx.prisma.user.findMany({
select: { id: true, name: true, email: true },
});
// ❌ 避免获取不必要的数据
const users = await ctx.prisma.user.findMany(); // 获取所有字段
// ✅ 使用分页
const users = await ctx.prisma.user.findMany({
take: 10,
skip: (page - 1) * 10,
orderBy: { createdAt: 'desc' },
});
// ✅ 监控慢查询
// 在配置中启用 queryMonitor 可自动检测 N+1 查询问题3. 生产环境部署
// config/config.prod.ts
export default {
teggPrisma: {
client: {
// DATABASE_URL 从 .env 或 schema.prisma 中配置 // 在 URL 中包含池设置
log: ['error'], // 生产环境只记录错误
queryMonitor: {
enabled: true,
slowQueryThreshold: 2000, // 生产环境更高的阈值
onSlowQuery: (info) => {
// 发送到监控服务(如 Sentry、DataDog)
monitoring.trackSlowQuery({
model: info.model,
action: info.action,
duration: info.duration,
});
},
},
transactionOptions: {
maxWait: 10000,
timeout: 30000,
isolationLevel: 'ReadCommitted',
},
}
}
};配置选项参考
完整配置
export default {
teggPrisma: {
// 在 app 进程中启用(默认: true)
app: true,
// 在 agent 进程中启用(默认: false)
agent: false,
// 所有客户端的默认选项
default: {
log: ['error', 'warn'],
},
// 单客户端配置
client: {
// 数据库 URL
// DATABASE_URL 从 .env 或 schema.prisma 中配置
// 日志配置
log: ['query', 'error', 'warn'],
// 或使用 emit 模式
log: [
{ emit: 'event', level: 'query' },
{ emit: 'stdout', level: 'error' },
],
// 错误格式
errorFormat: 'colorless', // 'pretty' | 'colorless' | 'minimal'
// Driver Adapter(Prisma 7.x)
// 用于动态数据库配置
adapter: myAdapter, // driver adapter 实例
// 示例:
// import { PrismaPg } from '@prisma/adapter-pg';
// const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
// 连接池 - 请在 DATABASE_URL 中配置:
// postgresql://...?connection_limit=10&pool_timeout=30
// 事务选项
transactionOptions: {
maxWait: 5000,
timeout: 10000,
isolationLevel: 'ReadCommitted',
},
// 查询监控
queryMonitor: {
enabled: true,
slowQueryThreshold: 1000,
logAllQueries: false,
onSlowQuery: (info) => {
console.warn('慢查询:', info);
},
onQuery: (info) => {
console.log('查询:', info);
},
},
// 自定义 Prisma Client
PrismaClient: CustomPrismaClient,
},
// 或多客户端配置
clients: {
db1: {
// 使用自定义 PrismaClient(从 schema-db1.prisma 生成)
// ... 与单客户端相同的选项
},
db2: {
// 使用自定义 PrismaClient(从 schema-db2.prisma 生成)
// ... 与单客户端相同的选项
},
},
}
};故障排除
问题:"PrismaClient is unable to run in this browser environment"
确保你没有为浏览器打包 Prisma Client。Prisma 只能在 Node.js 中运行。
问题:"@prisma/client did not initialize yet"
安装 @prisma/client 后运行 npx prisma generate。
问题:数据库连接问题
检查 .env 文件中的 DATABASE_URL:
DATABASE_URL="postgresql://user:password@localhost:5432/mydb?connection_limit=10"问题:慢查询
启用查询监控以检测慢查询:
queryMonitor: {
enabled: true,
slowQueryThreshold: 1000,
logAllQueries: process.env.NODE_ENV === 'development',
}问题:连接池耗尽
在 DATABASE_URL 中增加连接限制:
# 在连接字符串中增加连接限制
DATABASE_URL="postgresql://...?connection_limit=20&pool_timeout=60"API 参考
导出的工具
// 错误处理
import {
isPrismaError,
getPrismaErrorMessage,
PrismaErrorCodes,
getUniqueConstraintField,
} from '@gulibs/tegg-prisma';
// 事务工具
import { withTransaction } from '@gulibs/tegg-prisma';
import type { TransactionOptions } from '@gulibs/tegg-prisma';
// 查询监控类型
import type { QueryMonitorOptions, QueryInfo } from '@gulibs/tegg-prisma';
// 配置类型
import type {
EggPrismaConfig,
EggPrismaClientOption
} from '@gulibs/tegg-prisma';
// 客户端名称 symbol
import { CLIENT_NAME_SYMBOL } from '@gulibs/tegg-prisma';Application 方法
// 健康检查
await app.checkPrismaHealth(clientName?: string): Promise<boolean>
await app.getPrismaHealthStatus(): Promise<Record<string, boolean>>
// 原生查询
await app.prismaRawQuery<T>(sql: TemplateStringsArray, ...values: any[]): Promise<T[]>
// 指标
await app.getPrismaMetrics(clientName?: string): Promise<any>错误码
PrismaErrorCodes.UNIQUE_CONSTRAINT // P2002
PrismaErrorCodes.FOREIGN_KEY_CONSTRAINT // P2003
PrismaErrorCodes.RECORD_NOT_FOUND // P2025
PrismaErrorCodes.RELATION_VIOLATION // P2014
PrismaErrorCodes.DATABASE_UNREACHABLE // P1001
PrismaErrorCodes.DATABASE_TIMEOUT // P1008
PrismaErrorCodes.SERVER_CLOSED // P1017迁移指南
从 egg-sequelize 迁移
主要区别:
- Schema 定义:Prisma 使用
schema.prisma,而非模型文件 - 代码生成:修改 schema 后运行
npx prisma generate - 类型安全:Prisma 提供开箱即用的更好类型推断
- API:Prisma 有更现代化的 fluent API
- 健康检查:内置健康检查 API
- 监控:内置查询性能监控
示例迁移:
// Sequelize
const users = await ctx.model.User.findAll({
where: { age: { $gt: 18 } },
include: [{ model: ctx.model.Post }],
});
// Prisma
const users = await ctx.prisma.user.findMany({
where: { age: { gt: 18 } },
include: { posts: true },
});迁移到 Prisma 的好处:
- ✅ 更好的类型安全和自动补全
- ✅ 现代化查询 API
- ✅ 内置迁移工具
- ✅ 查询性能监控
- ✅ 健康检查 API
- ✅ 事务自动重试
- ✅ 更好的错误处理
许可证
MIT
文档
- TYPE-SAFETY.md - 类型安全最佳实践
- WHY-ONLY-AUGMENT-EGG.md - TypeScript 模块增强详解
链接
更新日志
查看 CHANGELOG.md 了解版本历史和迁移指南。
