11-3 Prisma与nestjs集成
Prisma环境配置
初始化Prisma项目
npm install prisma
npx prisma init
bash
- 创建
prisma/schema.prisma
配置文件
这是Prisma的核心配置文件,用于定义数据模型、数据库连接和生成器配置。默认生成的schema.prisma
包含以下部分:generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") }
prismagenerator
:定义生成的客户端类型(如JavaScript/TypeScript)。datasource
:指定数据库类型和连接信息。
- 生成
.env
环境变量文件.env
文件用于存储敏感信息(如数据库密码),默认内容如下:DATABASE_URL="mysql://root:example@localhost:3306/testdb"
dotenv
注意:.env
文件应添加到.gitignore
中,避免敏感信息泄露。 - 安装Prisma VSCode插件
插件名称:Prisma,提供以下功能:- 语法高亮
- 代码自动补全
- 错误检查
- 快速跳转到定义
💡提示:Prisma支持多种数据库,包括:
- PostgreSQL
- MySQL
- SQLite
- MongoDB
- SQL Server
- CockroachDB
数据库连接配置
在.env
文件中配置MySQL连接:
DATABASE_URL="mysql://root:example@localhost:3306/testdb"
dotenv
在schema.prisma
中指定数据库类型:
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
prisma
连接字符串详解
连接字符串格式为:协议://用户名:密码@主机:端口/数据库名?参数
示例:
DATABASE_URL="mysql://user:password@localhost:3306/mydb?connection_limit=5&pool_timeout=10"
dotenv
- 参数说明:
connection_limit
:连接池大小。pool_timeout
:连接超时时间(秒)。- 其他参数参考Prisma官方文档。
常见问题
- 连接失败
- 检查数据库服务是否启动。
- 验证用户名和密码是否正确。
- 确保端口未被占用。
- SSL配置
如果需要SSL连接,可以在连接字符串中添加参数:DATABASE_URL="mysql://user:password@localhost:3306/mydb?sslaccept=strict"
dotenv - 多环境配置
为开发、测试和生产环境分别配置.env
文件:.env.development
.env.test
.env.production
💡提示:使用dotenv-cli
工具加载不同环境的配置:
dotenv -e .env.development -- npx prisma migrate dev
bash
实践案例
使用Docker快速启动MySQL
# docker-compose.yml
version: '3'
services:
mysql:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: testdb
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
yaml
启动命令:
docker-compose up -d
bash
验证连接
npx prisma migrate status
bash
如果连接成功,会显示当前数据库迁移状态。
延伸学习
- Prisma Migrate
用于数据库迁移和版本控制,命令:npx prisma migrate dev
bash - Prisma Studio
可视化数据库管理工具:npx prisma studio
bash - 多数据库支持
Prisma支持同时连接多个数据库,配置示例:datasource db1 { provider = "mysql" url = env("DATABASE_URL_1") } datasource db2 { provider = "postgresql" url = env("DATABASE_URL_2") }
prisma - 性能优化
- 使用连接池(如
connection_limit
参数)。 - 启用查询日志:
datasource db { provider = "mysql" url = env("DATABASE_URL") logging = ["query"] }
prisma
- 使用连接池(如
💡提示:更多配置参考Prisma官方文档。
数据库服务部署
Docker部署MySQL
详细配置说明
version: '3.8' # 推荐使用3.8版本以获得最佳兼容性
services:
mysql:
image: mysql:8.0 # 建议指定版本号而非latest
container_name: prisma-mysql # 自定义容器名称
ports:
- "3306:3306" # 主机端口:容器端口
environment:
MYSQL_ROOT_PASSWORD: example # root密码
MYSQL_DATABASE: testdb # 初始数据库
MYSQL_USER: devuser # 可选:创建普通用户
MYSQL_PASSWORD: devpass # 普通用户密码
volumes:
- mysql_data:/var/lib/mysql # 数据持久化
- ./mysql/conf.d:/etc/mysql/conf.d # 挂载自定义配置
healthcheck: # 健康检查
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 5s
timeout: 10s
retries: 5
networks:
- prisma-network # 自定义网络
volumes:
mysql_data: # 命名卷
networks:
prisma-network: # 创建专用网络
driver: bridge
yaml
关键配置解析
- 版本控制
- 使用
mysql:8.0
而非latest
避免版本冲突 - Compose文件版本
3.8
支持所有现代Docker功能
- 使用
- 数据持久化
volumes
确保数据库重启后数据不丢失- 自定义配置目录可添加
my.cnf
优化性能
- 健康检查
自动监控数据库状态,确保服务就绪后才进行后续操作
启动命令详解
# 启动服务(后台模式)
docker-compose up -d
# 查看运行日志
docker-compose logs -f mysql
# 停止服务(保留数据卷)
docker-compose stop
# 完全删除(含数据卷)
docker-compose down -v
bash
创建数据库
方法1:使用phpMyAdmin
- 部署phpMyAdmin
在docker-compose.yml
中添加:phpmyadmin: image: phpmyadmin/phpmyadmin ports: - "8080:80" environment: PMA_HOST: mysql PMA_PORT: 3306 depends_on: - mysql networks: - prisma-network
yaml - 操作流程
- 访问
http://localhost:8080
- 登录凭证:
- 用户名:
root
- 密码:
example
- 用户名:
- 在UI中创建
testdb
数据库
- 访问
方法2:命令行操作
# 进入MySQL容器
docker exec -it prisma-mysql mysql -uroot -pexample
# 执行SQL命令
CREATE DATABASE IF NOT EXISTS testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
# 验证创建结果
SHOW DATABASES;
bash
方法3:使用Prisma直接初始化
npx prisma db push
bash
该命令会:
- 自动创建数据库(如果不存在)
- 根据schema.prisma生成表结构
- 应用所有数据模型变更
验证连接状态
测试方式
- Prisma验证
npx prisma migrate status
bash
成功输出示例:Database connection successful No pending migrations.
text - 手动连接测试
docker run -it --rm --network prisma-network mysql:8.0 mysql -hmysql -uroot -pexample -e "SELECT 1"
bash - 可视化工具推荐
常见问题解决
连接失败排查
性能优化建议
- MySQL配置调优
在挂载的conf.d/my.cnf
中添加:[mysqld] innodb_buffer_pool_size = 1G # 根据内存调整 max_connections = 200
ini - 连接池配置
在Prisma的DATABASE_URL中添加参数:DATABASE_URL="mysql://root:example@mysql:3306/testdb?connection_limit=20&pool_timeout=5"
dotenv
扩展实践:多环境部署
生产环境配置示例
# docker-compose.prod.yml
services:
mysql:
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: ${DB_NAME}
deploy:
resources:
limits:
cpus: '2'
memory: 4G
restart: always
yaml
启动命令:
DB_ROOT_PASSWORD=securepass DB_NAME=prod_db docker-compose -f docker-compose.prod.yml up -d
bash
💡提示:生产环境务必:
- 使用强密码
- 启用定期备份
- 配置监控告警
Prisma数据建模详解
Schema定义深度解析
基础模型定义
model User {
id Int @id @default(autoincrement())
username String @unique
password String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
prisma
字段属性详解
属性 | 说明 | 示例 |
---|---|---|
@id | 主键标识 | id Int @id |
@default | 默认值 | @default(autoincrement()) |
@unique | 唯一约束 | username String @unique |
@map | 字段映射 | @map("user_name") |
@updatedAt | 自动更新时间 | updatedAt DateTime @updatedAt |
? | 可选字段 | String? |
高级字段类型
model Product {
id Int @id @default(autoincrement())
name String
price Decimal @default(0.0)
tags String[]
metadata Json
isAvailable Boolean @default(false)
}
prisma
关系建模
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
prisma
同步模型到数据库
完整工作流
关键命令对比
命令 | 作用 | 适用场景 |
---|---|---|
prisma db push | 直接同步模型 | 开发环境快速迭代 |
prisma migrate dev | 创建迁移记录 | 需要版本控制的场景 |
prisma migrate reset | 重置数据库 | 测试环境数据清理 |
生产环境最佳实践
- 使用迁移工具
npx prisma migrate dev --name init npx prisma migrate deploy
bash - SQL审核流程
npx prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script
bash - 数据备份策略
# 导出数据结构 npx prisma migrate export --preview-feature
bash
验证数据库结构
命令行验证
# 查看当前数据库状态
npx prisma db execute --file ./prisma/verify.sql --schema prisma/schema.prisma
bash
verify.sql
示例:
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'testdb';
sql
可视化工具验证
- Prisma Studio
npx prisma studio
bash
访问http://localhost:5555
- 数据库客户端验证
DESCRIBE User; SHOW INDEX FROM User;
sql
常见问题解决
模型同步失败排查
典型错误案例
- 字段类型冲突
// 错误:尝试将String改为Int model User { id String @id // 原为Int类型 }
prisma
解决方案:npx prisma migrate reset
bash - 关系循环引用
model A { id Int @id b B[] } model B { id Int @id a A[] }
prisma
解决方案:添加中间模型
性能优化技巧
- 索引优化
model User { id Int @id email String @unique @@index([createdAt]) }
prisma - 批量操作
// 批量插入 await prisma.user.createMany({ data: [...] })
typescript - 查询优化
model Post { title String @@fulltext([title]) }
prisma
扩展学习资源
💡提示:开发过程中建议安装prisma-extension
VSCode插件,提供实时错误检查和代码补全功能。
NestJS集成Prisma深度实践
模块化工程架构
推荐项目结构
src/
├── database/
│ ├── prisma/
│ │ ├── prisma.module.ts
│ │ ├── prisma.service.ts
│ │ └── interfaces/
├── users/
└── app.module.ts
text
生成命令优化
# 使用--flat避免嵌套目录(简单项目)
nest g mo database/prisma --flat
nest g s database/prisma --flat
# 使用--no-spec跳过测试文件生成
nest g s database/prisma --no-spec
bash
Prisma服务高级封装
增强版Prisma服务
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
constructor() {
super({
log: ['query', 'info', 'warn', 'error'],
});
}
async onModuleInit() {
await this.$connect();
}
async onModuleDestroy() {
await this.$disconnect();
}
// 添加事务扩展方法
async transactional<T>(fn: (prisma: PrismaClient) => Promise<T>): Promise<T> {
return this.$transaction(fn);
}
}
typescript
关键增强功能
- 完整的生命周期管理
OnModuleInit
:初始化连接OnModuleDestroy
:关闭连接
- 查询日志配置
- 开发环境显示完整SQL日志
- 生产环境建议只保留error日志
- 事务封装
- 提供统一的事务处理接口
模块注册最佳实践
动态配置模块
import { Module, Global } from '@nestjs/common';
import { PrismaService } from './prisma.service';
@Global() // 声明为全局模块
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class PrismaModule {}
typescript
配置项说明
装饰器 | 作用 | 使用场景 |
---|---|---|
@Global() | 全局模块 | 需要跨模块使用的服务 |
@Module() | 标准模块 | 普通功能模块 |
多环境配置集成
环境变量加载
import { ConfigService } from '@nestjs/config';
@Injectable()
export class PrismaService extends PrismaClient {
constructor(config: ConfigService) {
super({
datasources: {
db: {
url: config.get('DATABASE_URL'),
},
},
});
}
}
typescript
对应.env文件
# 开发环境
DATABASE_URL="mysql://root:devpass@localhost:3306/devdb"
# 测试环境
DATABASE_URL="mysql://testuser:testpass@testdb:3306/testdb"
dotenv
异常处理增强
自定义Prisma异常过滤器
import { Catch, ArgumentsHost } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { Prisma } from '@prisma/client';
@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaClientExceptionFilter extends BaseExceptionFilter {
catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
switch (exception.code) {
case 'P2002':
response.status(409).json({
code: 'CONFLICT',
message: '唯一约束冲突',
});
break;
default:
super.catch(exception, host);
}
}
}
typescript
注册全局过滤器
// main.ts
app.useGlobalFilters(new PrismaClientExceptionFilter());
typescript
性能优化方案
连接池配置
// prisma.service.ts
super({
datasources: {
db: {
url: config.get('DATABASE_URL') + '?connection_limit=20',
},
},
});
typescript
查询优化技巧
// 使用select减少字段返回
await this.prisma.user.findMany({
select: {
id: true,
name: true,
},
});
// 使用include关联查询
await this.prisma.post.findMany({
include: {
author: true,
},
});
typescript
测试策略
单元测试配置
describe('PrismaService', () => {
let service: PrismaService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PrismaService],
}).compile();
service = module.get<PrismaService>(PrismaService);
});
it('should connect on module init', async () => {
await service.onModuleInit();
expect(service).toBeDefined();
});
});
typescript
测试数据库配置
// test/prisma-test.service.ts
@Injectable()
export class PrismaTestService extends PrismaClient {
constructor() {
super({
datasources: {
db: {
url: 'mysql://root:testpass@localhost:3306/testdb',
},
},
});
}
}
typescript
扩展应用场景
GraphQL集成
// users.resolver.ts
@Resolver()
export class UsersResolver {
constructor(private prisma: PrismaService) {}
@Query(() => [User])
async users() {
return this.prisma.user.findMany();
}
}
typescript
RESTful API示例
// users.controller.ts
@Controller('users')
export class UsersController {
constructor(private prisma: PrismaService) {}
@Get()
async findAll() {
return this.prisma.user.findMany();
}
}
typescript
监控与日志
查询日志收集
// 配置日志钩子
this.$on('query' as never, (e: any) => {
logger.debug(`Query: ${e.query} | Duration: ${e.duration}ms`);
});
typescript
Prometheus监控
import { Counter } from 'prom-client';
const prismaQueryCounter = new Counter({
name: 'prisma_query_total',
help: 'Total number of Prisma queries',
labelNames: ['model', 'operation'],
});
// 在查询方法中添加计数
this.prisma.$use(async (params, next) => {
prismaQueryCounter.inc({
model: params.model,
operation: params.action,
});
return next(params);
});
typescript
💡提示:生产环境建议结合Prisma Pulse实现实时数据变更监听
数据库操作实践进阶指南
控制器深度集成
完整RESTful控制器示例
import { Controller, Get, Post, Body, Param, Delete, Patch } from '@nestjs/common';
import { PrismaService } from './database/prisma/prisma.service';
import { CreateUserDto, UpdateUserDto } from './dto';
@Controller('users')
export class UsersController {
constructor(private readonly prisma: PrismaService) {}
@Get()
async findAll() {
return this.prisma.user.findMany({
select: {
id: true,
username: true,
createdAt: true
}
});
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.prisma.user.findUnique({
where: { id: Number(id) },
include: {
posts: true
}
});
}
@Post()
async create(@Body() createUserDto: CreateUserDto) {
return this.prisma.user.create({
data: createUserDto
});
}
@Patch(':id')
async update(
@Param('id') id: string,
@Body() updateUserDto: UpdateUserDto
) {
return this.prisma.user.update({
where: { id: Number(id) },
data: updateUserDto
});
}
@Delete(':id')
async remove(@Param('id') id: string) {
return this.prisma.user.delete({
where: { id: Number(id) }
});
}
}
typescript
查询优化技巧
- 分页查询实现
@Get()
async findPaginated(
@Query('page') page = 1,
@Query('limit') limit = 10
) {
const skip = (page - 1) * limit;
return this.prisma.user.findMany({
skip,
take: limit,
orderBy: { createdAt: 'desc' }
});
}
typescript
- 条件筛选查询
@Get('search')
async searchUsers(@Query('keyword') keyword: string) {
return this.prisma.user.findMany({
where: {
OR: [
{ username: { contains: keyword } },
{ email: { contains: keyword } }
]
}
});
}
typescript
错误处理全面方案
常见错误类型及处理
错误代码 | 描述 | 解决方案 |
---|---|---|
P2002 | 唯一约束冲突 | 返回409 Conflict状态码 |
P2025 | 记录不存在 | 返回404 Not Found |
P2003 | 外键约束失败 | 检查关联数据完整性 |
P2016 | 查询解析错误 | 验证查询条件格式 |
全局异常过滤器增强
import { Catch, ArgumentsHost, HttpStatus } from '@nestjs/common';
import { BaseExceptionFilter } from '@nestjs/core';
import { Prisma } from '@prisma/client';
@Catch(Prisma.PrismaClientKnownRequestError)
export class PrismaExceptionFilter extends BaseExceptionFilter {
catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const errorMap = {
P2002: {
status: HttpStatus.CONFLICT,
message: '资源已存在'
},
P2025: {
status: HttpStatus.NOT_FOUND,
message: '资源不存在'
},
default: {
status: HttpStatus.INTERNAL_SERVER_ERROR,
message: '数据库操作异常'
}
};
const errorConfig = errorMap[exception.code] || errorMap.default;
response.status(errorConfig.status).json({
errorCode: exception.code,
message: errorConfig.message,
meta: exception.meta
});
}
}
typescript
在main.ts中注册
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new PrismaExceptionFilter());
await app.listen(3000);
}
typescript
事务处理实践
基础事务示例
@Post('transfer')
async transferFunds(
@Body() transferDto: TransferDto
) {
return this.prisma.$transaction(async (tx) => {
// 扣款
await tx.account.update({
where: { id: transferDto.fromAccount },
data: { balance: { decrement: transferDto.amount } }
});
// 存款
await tx.account.update({
where: { id: transferDto.toAccount },
data: { balance: { increment: transferDto.amount } }
});
// 记录交易
return tx.transaction.create({
data: {
amount: transferDto.amount,
fromAccount: transferDto.fromAccount,
toAccount: transferDto.toAccount
}
});
});
}
typescript
事务隔离级别配置
await this.prisma.$transaction([
this.prisma.user.create({ data: user1 }),
this.prisma.user.create({ data: user2 })
], {
isolationLevel: Prisma.TransactionIsolationLevel.Serializable
});
typescript
性能监控与优化
查询性能分析
// 在PrismaService中添加
this.$on('query' as never, (e) => {
performance.mark('query-start');
performance.mark('query-end');
performance.measure('query-duration', 'query-start', 'query-end');
});
typescript
缓存策略实现
@Get(':id')
@CacheTTL(60) // 缓存60秒
async getUserWithCache(@Param('id') id: string) {
return this.prisma.user.findUnique({
where: { id: Number(id) }
});
}
typescript
测试策略完善
集成测试示例
describe('UsersController (e2e)', () => {
let app: INestApplication;
let prisma: PrismaService;
beforeAll(async () => {
const moduleFixture = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
prisma = moduleFixture.get<PrismaService>(PrismaService);
await app.init();
});
afterEach(async () => {
await prisma.user.deleteMany();
});
it('/POST users', () => {
return request(app.getHttpServer())
.post('/users')
.send({ username: 'test', email: 'test@example.com' })
.expect(201);
});
});
typescript
安全最佳实践
数据验证装饰器
@Post()
async createUser(
@Body() @Validate(CreateUserValidator) createUserDto: CreateUserDto
) {
// 业务逻辑
}
typescript
SQL注入防护
// 使用Prisma的参数化查询自动防护
await this.prisma.$queryRaw`
SELECT * FROM User WHERE username = ${username}
`;
typescript
扩展阅读推荐
测试验证与调试全流程指南
完整测试验证流程
1. 应用启动与测试
# 开发模式启动(带热重载)
npm run start:dev
# 生产模式启动
npm run start:prod
# 测试模式启动
npm run test:e2e
bash
2. 接口测试方法
使用cURL测试:
curl -X GET http://localhost:3000/users \
-H "Content-Type: application/json"
bash
使用Postman测试:
- 创建新请求 → GET →
http://localhost:3000/users
- 添加Header:
Content-Type: application/json
- 发送请求
预期响应:
{
"statusCode": 200,
"data": [
{
"id": 1,
"username": "user1",
"createdAt": "2023-07-20T08:00:00.000Z"
},
{
"id": 2,
"username": "user2",
"createdAt": "2023-07-20T08:05:00.000Z"
}
]
}
json
3. 数据验证技巧
// 测试用例示例
it('should return user array', async () => {
const response = await request(app.getHttpServer())
.get('/users')
.expect(200);
expect(response.body).toHaveProperty('data');
expect(Array.isArray(response.body.data)).toBeTruthy();
expect(response.body.data[0]).toHaveProperty('username');
});
typescript
高级调试技巧
调试流程图解
关键调试节点
- 请求拦截层
// 全局日志中间件 app.use((req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); });
typescript - Prisma查询日志
// prisma.service.ts constructor() { super({ log: ['query', 'info', 'warn', 'error'] }); }
typescript - 响应拦截器
@Injectable() export class TransformInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler) { console.log('Before handler...'); return next.handle().pipe( tap(data => console.log('After handler:', data)) ); } }
typescript
常见问题排查
问题1:返回空数组
可能原因:
- 数据库无数据
- 连接配置错误
解决方案:
# 检查数据库连接
npx prisma migrate status
# 插入测试数据
npx prisma db seed
bash
问题2:字段未返回
可能原因:
- 查询未包含字段
- 字段权限限制
修正方案:
// 显式指定返回字段
await this.prisma.user.findMany({
select: {
id: true,
username: true
}
});
typescript
性能监控实践
查询耗时分析
// 在PrismaService中添加
this.$on('query', (e) => {
console.log(`Query: ${e.query} | Duration: ${e.duration}ms`);
});
typescript
API性能测试
# 使用autocannon压测
npx autocannon -c 100 -d 60 http://localhost:3000/users
bash
安全测试要点
- 敏感字段过滤
// 排除password字段 await this.prisma.user.findMany({ select: { id: true, username: true, // password: false } });
typescript - SQL注入测试
# 尝试注入攻击 curl "http://localhost:3000/users?where=1=1;DROP TABLE User"
bash - 速率限制验证
// 添加限流中间件 app.use( rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }) );
typescript
扩展调试工具
工具 | 用途 | 安装命令 |
---|---|---|
NestJS Debugger | 框架级调试 | VS Code内置 |
Prisma Studio | 数据可视化 | npx prisma studio |
Wireshark | 网络包分析 | brew install wireshark |
Postman Interceptor | 请求捕获 | Chrome扩展 |
自动化测试脚本
// test/users.e2e-spec.js
const { test, expect } = require('@playwright/test');
test('API测试', async ({ request }) => {
const response = await request.get('http://localhost:3000/users');
expect(response.ok()).toBeTruthy();
const users = await response.json();
expect(users.length).toBeGreaterThan(0);
});
javascript
💡提示:结合CI/CD流水线实现自动化测试:
# GitHub Actions示例
jobs:
test:
steps:
- run: npm run test:e2e
- uses: actions/upload-artifact@v2
if: failure()
with:
name: test-results
path: test-results/
yaml
Prisma全栈开发进阶指南
Prisma完整工作流详解
可视化开发流程
关键工具链集成
阶段 | 工具 | 作用 |
---|---|---|
建模 | Prisma VSCode插件 | 实时Schema校验 |
开发 | Prisma Studio | 可视化数据操作 |
测试 | Jest+Prisma Client Mock | 单元测试模拟 |
部署 | Prisma Migrate | 版本化数据库变更 |
用户认证系统实现
JWT认证方案
// auth.service.ts
@Injectable()
export class AuthService {
constructor(private prisma: PrismaService) {}
async validateUser(username: string, pass: string): Promise<any> {
const user = await this.prisma.user.findUnique({
where: { username }
});
if (user && compareSync(pass, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload),
};
}
}
typescript
安全增强措施
- 密码加密
npm install bcrypt npm install @types/bcrypt -D
bash - 速率限制
@Throttle(5, 60) // 60秒内最多5次请求 @Post('login') async login(@Body() dto: LoginDto) { ... }
typescript
关系型数据建模实战
一对多关系示例
model User {
id Int @id @default(autoincrement())
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
prisma
复杂查询示例
// 获取用户及其最近5篇文章
await this.prisma.user.findUnique({
where: { id: 1 },
include: {
posts: {
take: 5,
orderBy: { createdAt: 'desc' }
}
}
});
typescript
事务处理高级模式
银行转账案例
async transferFunds(fromId: number, toId: number, amount: number) {
return this.prisma.$transaction(async (tx) => {
// 1. 验证账户余额
const fromAccount = await tx.account.findUnique({
where: { id: fromId },
select: { balance: true }
});
if (fromAccount.balance < amount) {
throw new Error('余额不足');
}
// 2. 执行转账
await tx.account.update({
where: { id: fromId },
data: { balance: { decrement: amount } }
});
await tx.account.update({
where: { id: toId },
data: { balance: { increment: amount } }
});
// 3. 记录交易
return tx.transaction.create({
data: {
amount,
fromId,
toId,
status: 'COMPLETED'
}
});
});
}
typescript
数据库迁移工程化
迁移工作流
# 开发环境
npx prisma migrate dev --name init
# 生产环境
npx prisma migrate deploy
# 回滚迁移
npx prisma migrate resolve --rolled-back <migration_name>
bash
迁移策略对比
策略 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
自动迁移 | 开发环境 | 快速迭代 | 不适合生产 |
影子迁移 | CI测试 | 安全验证 | 需要额外数据库 |
蓝绿部署 | 生产环境 | 零停机 | 基础设施复杂 |
多数据库方案集成
MongoDB集成示例
// mongo.module.ts
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/nest'),
MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }])
]
})
export class MongoModule {}
typescript
多数据库混用策略
性能优化方案
查询优化技巧
- 批量插入
await this.prisma.user.createMany({ data: userList, skipDuplicates: true });
typescript - 索引优化
model User { email String @unique @@index([createdAt]) }
prisma
缓存策略
// 使用Redis缓存
@Injectable()
export class UserService {
constructor(
private prisma: PrismaService,
@Inject(CACHE_MANAGER) private cacheManager: Cache
) {}
async getUser(id: number) {
const cached = await this.cacheManager.get(`user_${id}`);
if (cached) return cached;
const user = await this.prisma.user.findUnique({ where: { id } });
await this.cacheManager.set(`user_${id}`, user, 60);
return user;
}
}
typescript
扩展学习路线
推荐学习路径
- 初级
- Prisma官方文档基础篇
- NestJS CRUD实现
- 中级
- 关系型数据建模
- 事务处理实战
- 高级
- 分布式事务
- 多租户架构
优质资源
类型 | 资源 | 特点 |
---|---|---|
视频 | Prisma Masterclass | 官方互动课程 |
书籍 | 《Prisma实战》 | 中文社区推荐 |
项目 | Next.js+Prisma全栈示例 | 生产级参考 |
💡提示:建议结合Prisma Day年度会议了解最新技术动态,参与Prisma社区获取实战经验分享。
↑