当前位置: 首页 > news >正文

从0死磕全栈第十天:nest.js集成prisma完成CRUD

前言:为什么选择 Nest.js + Prisma?

在全栈开发中,后端选型直接影响开发效率与项目可维护性。
如果你是 TypeScript 开发者,又想摆脱“手写 SQL + 类型不安全”的噩梦,那么 Nest.js + Prisma 是你不可错过的技术组合:

  • Nest.js:基于 TypeScript 的企业级 Node.js 框架,借鉴 Spring 的模块化、装饰器、依赖注入,结构清晰,易于团队协作。
  • Prisma:现代 ORM,通过声明式 schema 自动生成类型安全的数据库客户端,告别拼接 SQL、类型错乱、手动映射。

本文将手把手带你从零搭建一个完整的 Nest.js 后端服务,包含:

  • ✅ Prisma 初始化与数据库迁移
  • ✅ 用户 CRUD 操作
  • ✅ 分页查询 + 条件搜索
  • ✅ 时间格式化拦截器(解决 2025-08-09T05:54:14.508Z 问题)

✅ 第一步:安装 Prisma 依赖

在你的 Nest.js 项目根目录下执行:

npm install prisma @prisma/client
  • prisma:命令行工具,用于初始化、迁移、生成客户端
  • @prisma/client:运行时客户端,供服务层调用

✅ 第二步:初始化 Prisma 项目

执行以下命令,生成 Prisma 配置文件:

npx prisma init

该命令会在项目中创建两个关键文件:

1. .env 环境配置文件

# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="file:./dev.db"

💡 为快速上手,我们使用轻量级的 SQLite 数据库。生产环境推荐 PostgreSQL 或 MySQL。

2. prisma/schema.prisma 数据模型定义文件

generator client {provider = "prisma-client-js"
}datasource db {provider = "sqlite"url      = "file:./dev.db"
}model User {id        Int      @id @default(autoincrement())email     String   @uniquename      String?password  StringcreatedAt DateTime @default(now())updatedAt DateTime @updatedAt
}

@id:主键
@unique:唯一索引
@updatedAt:自动更新时间戳
@default(now()):默认当前时间


✅ 第三步:执行数据库迁移与生成客户端

1. 创建并应用迁移

npx prisma migrate dev --name init
  • 自动创建 dev.db 文件(SQLite 数据库)
  • prisma/migrations 目录下生成迁移历史文件
  • User 表结构写入数据库

2. 生成 Prisma Client

npx prisma generate
  • 自动生成 node_modules/.prisma/client/ 目录
  • 生成 完全类型安全PrismaClient 实例,支持智能提示

✅ 第四步:创建 Prisma 模块与服务

1. 生成 Prisma 模块和服务

nest generate module prisma
nest generate service prisma

2. 编写 PrismaServicesrc/prisma/prisma.service.ts

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();}
}

✅ 继承 PrismaClient,实现 OnModuleInitOnModuleDestroy,确保连接在模块初始化时打开,销毁时关闭。

3. 定义 PrismaModulesrc/prisma/prisma.module.ts

import { Global, Module } from '@nestjs/common';
import { PrismaService } from './prisma.service';@Global()
@Module({providers: [PrismaService],exports: [PrismaService], // 导出供其他模块注入使用
})
export class PrismaModule {}

✅ 使用 @Global() 使该模块在任何地方都可被导入,无需重复导入。


✅ 第五步:在 Users 模块中使用 Prisma

1. 创建 DTO(数据传输对象)src/users/dto/create-user.dto.ts

export class CreateUserDto {email: string;name: string;password: string;
}

✅ DTO 用于定义接口接收的参数结构,提升类型安全与接口文档清晰度。

2. 编写 Users 服务 src/users/users.service.ts

import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';@Injectable()
export class UsersService {constructor(private prisma: PrismaService) {}async create(user: CreateUserDto) {console.log('create user');return this.prisma.user.create({ data: user });}async findAll() {return this.prisma.user.findMany();}
}

this.prisma.user.create():自动映射到数据库表,参数类型安全,IDE 有智能提示!

3. 编写控制器 src/users/users.controller.ts

import { Controller, Get, Post, Body } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';@Controller('users')
export class UsersController {constructor(private readonly usersService: UsersService) {}@Post()create(@Body() createUserDto: CreateUserDto) {return this.usersService.create(createUserDto);}@Get()findAll() {return this.usersService.findAll();}
}

@Post() / @Get():Nest.js 的路由装饰器,语义清晰,类似 Spring 的 @RequestMapping


✅ 第六步:测试接口(推荐使用 VS Code 插件:REST Client)

安装 VS Code 插件:REST Client(作者:Huachao Mao)

创建文件 test.http

### 创建用户
POST http://localhost:3000/users
Content-Type: application/json{"email": "er11@example.com","name": "hu","password": "123456"
}### 查询所有用户
GET http://localhost:3000/users

✅ 选中请求块 → 按 Ctrl+Alt+R(Windows)或 Cmd+Alt+R(Mac)直接发送请求
✅ 支持高亮、历史记录、环境变量,是调试 API 的神器!

⚠️ 目前尚无插件能自动识别 Nest.js 路由生成 .http 文件,需手动编写。


✅ 第七步:实现分页查询 + 条件搜索

1. 在 UsersService 中添加分页方法

async findByPageWithName(page: number = 1,pageSize: number = 10,name?: string
) {const skip = (page - 1) * pageSize;const take = pageSize;const where = name? {OR: [{ name: { contains: name } },],}: {};const [users, total] = await Promise.all([this.prisma.user.findMany({skip,take,where,}),this.prisma.user.count({ where }),]);return {users,total,page,pageSize,totalPages: Math.ceil(total / pageSize),};
}

Promise.all 并发查询数据和总数,提升性能
contains:模糊搜索(LIKE ‘%xxx%’)
✅ 返回结构标准化,前端可直接渲染分页器

2. 在控制器中暴露接口

import { Query, ParseIntPipe, DefaultValuePipe } from '@nestjs/common';// ... 省略其他代码@Get('page')
async getByPage(@Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,@Query('pageSize', new DefaultValuePipe(10), ParseIntPipe) pageSize: number,@Query('name') name?: string,
) {return this.usersService.findByPageWithName(page, pageSize, name);
}

@Query():获取 URL 查询参数
DefaultValuePipe:设置默认值
ParseIntPipe:自动转换字符串为数字,防止类型错误

✅ 测试分页接口

### 分页查询(带搜索)
GET http://localhost:3000/users/page?page=1&pageSize=5&name=hu

✅ 第八步:解决时间格式问题 —— 创建时间格式化拦截器

❌ 问题:返回时间是 2025-08-09T05:54:14.508Z

这不是我们想要的 2025-08-09 13:54:14 格式。

✅ 解决方案:使用 Nest.js 拦截器(Interceptor)

1. 创建拦截器目录与文件
mkdir src/interceptors
touch src/interceptors/transform.interceptor.ts
2. 编写 TransformInterceptor
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';@Injectable()
export class TransformInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {return next.handle().pipe(map((data) => this.transformDates(data)),);}private transformDates(data: any): any {if (!data) return data;// 如果是数组,递归处理每个元素if (Array.isArray(data)) {return data.map((item) => this.transformDates(item));}// 如果是对象,处理每个属性if (typeof data === 'object') {for (const key in data) {if (data.hasOwnProperty(key)) {// 处理日期类型if (data[key] instanceof Date) {data[key] = this.formatDate(data[key]);}// 递归处理嵌套对象else if (typeof data[key] === 'object' && data[key] !== null) {data[key] = this.transformDates(data[key]);}}}}return data;}// 格式化日期为 UTC+8 时区的 yyyy-MM-dd HH:mm:ssprivate formatDate(date: Date): string {const utc8Date = new Date(date.getTime() + 8 * 60 * 60 * 1000);return utc8Date.toISOString().replace(/T/, ' ')     // 替换 T 为空格.replace(/\..+/, '')   // 删除毫秒部分.slice(0, 19);         // 截取到秒}
}

✅ 支持:

  • 深度递归处理嵌套对象
  • 自动识别 Date 类型
  • 转换为本地时间(UTC+8)
  • 格式统一为 2025-08-09 13:54:14
3. 在 AppModule 中全局注册拦截器
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { PrismaModule } from './prisma/prisma.module';
import { UsersModule } from './users/users.module';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { TransformInterceptor } from './interceptors/transform.interceptor';@Module({imports: [PrismaModule, UsersModule],controllers: [AppController],providers: [AppService,{provide: APP_INTERCEPTOR,useClass: TransformInterceptor,},],
})
export class AppModule {}

APP_INTERCEPTOR 是 Nest.js 提供的全局拦截器 token,注册后所有接口响应都会经过此拦截器!


✅ 最终效果对比

时间格式未使用拦截器使用拦截器后
返回值2025-08-09T05:54:14.508Z2025-08-09 13:54:14

✅ 前端再也不用手动 new Date().toLocaleString(),后端统一输出,体验一致!


🧠 拦截器本质:装饰器的“元数据”魔法

Nest.js 的拦截器、装饰器(如 @Get()@Body())本质是 TypeScript 装饰器

📌 装饰器是一种特殊函数,它能给类、方法、参数添加“元数据”,告诉框架:“这个类是控制器”“这个方法处理 GET 请求”“这个参数来自查询字符串”。

它不改变核心业务逻辑,却能扩展功能、统一处理、提升可维护性


✅ 总结:Nest.js + Prisma 开发流程图

[定义模型] → [npx prisma init] → [npx prisma migrate dev] → [npx prisma generate]↓
[创建 PrismaService] → [注册 PrismaModule] → [在 Service 中使用 this.prisma.user.xxx()]↓
[定义 DTO] → [编写 Controller] → [测试接口(REST Client)] → [添加分页/搜索]↓
[创建 TransformInterceptor] → [全局注册] → [统一时间格式输出]

🚀 推荐资源

  • Nest.js 官网:https://docs.nestjs.com
  • Prisma 官网:https://www.prisma.io
  • VS Code REST Client 插件:https://marketplace.visualstudio.com/items?itemName=humao.rest-client

💬 结语

别再用 Express 写 SQL 了!
用 Nest.js + Prisma,你写的不是“后端接口”,而是类型安全、结构清晰、可维护的工业级服务

从今天起,告别拼接字符串、类型错误、时间格式混乱,
拥抱现代 TypeScript 全栈开发的优雅与高效!

http://www.dtcms.com/a/398366.html

相关文章:

  • 网站开发做什么科目网页设计与网站建设连接数据库
  • 如何看网站是html几代做的加拿大pc网站搭建
  • C#的MVVM架构中的几种数据绑定方式
  • Jmeter接口测试:jmeter组件元件介绍,利用取样器中http发送请求
  • Apache Tomcat 部署与配置
  • 网站建设详细合同范本西部数码网站管理助手破解版
  • 权限提升专项训练靶场:hacksudo: L.P.E.
  • 工作笔记----lwip的数据管理结构pbuf源码解析
  • 生产环境实战:Spring Cloud Sleuth与Zipkin分布式链路追踪实践
  • 学习React-15-useImperativeHandle
  • 响应式网站案列小学生做电子小报的网站
  • 【AskAI系列课程】:P4.将AI助手集成到Astro网站前端
  • 自注意力机制(Self-Attention)简介
  • App 代上架全流程解析 iOS 应用代上架服务、苹果应用发布步骤、ipa 文件上传与 App Store 审核经验
  • 学习日报 20250921|MQ (Kafka)面试深度复盘
  • 趣味学Solana(启航)
  • 期权末日论效应怎么来的?
  • iOS 混淆与反调试反 Hook 实战,运行时防护、注入检测与安全加固流程
  • 建设工程管理网站邹平建设网站
  • wordpress英文下主题怎么换苏州seo专家教优化网站结构
  • 《灼灼韶华》还原民国上海滩,虎鲸文娱虚拟拍摄让创作突破时空束缚
  • Redo Log 与 Crash Recovery:MySQL 事务持久化的核心技术
  • 金乡网站建设公司云南企业网站
  • 设计模式(C++)详解——职责链模式 (Chain of Responsibility)(1)
  • 酒店网站免费建设国际新闻今天最新
  • 企业产品网络安全日志9月23日-WAF应急
  • 嵌入式硬件工程师:绝缘栅型场效应管
  • HTTPS 请求抓包实战,从请求捕获到解密分析的逐步流程与工具组合(https 请求抓包、iOS 真机、SSL Pinning 排查)
  • 怎么学习cuda?
  • iOS 开发指南全解析 从入门到应用上架、Xcode 使用教程、ipa 打包上传与 App Store 审核实战经验