使用Cursor创建NestJS项目实录(2)用户模块与Prisma配置详解

文章首先本人公众号:Java小成

大家好,我是汪小成。

最近在常用NestJS,刚好有个做一个在线工具箱的想法。工具箱需要一个简单的后台服务,于是就使用NestJS开发了一个后台服务。借着这个简单的项目学习下NestJS。我会记录下使用NestJS创建后台服务的每一个过程,感兴趣的朋友可以关注下我的公众号Java小成

目前已经完成的工具如下:

JSON格式化 ------ 快速美化/压缩JSON数据

Base64编码/解码 ------ 轻松处理加密数据

时间戳转换 ------ 秒级时间戳 ↔ 可读日期

文本对比 ------ 高亮显示差异,排查代码/配置更轻松

二维码生成器 ------高度可定制 ,支持Logo、颜色、尺寸调整

URL编码/解码 ------ 一键处理特殊字符

UUID生成器 ------ 快速生成唯一标识符

JWT解析器 ------ 直接解码JWT Token,查看Payload

体检地址:https://ddcherry.cn/toolbox/

1、前言

在前面一章,使用Cursor创建了一个NestJS后台服务项目。接下来详细地拆解下项目。

2、技术栈

  • NestJS: v11.0.1
  • TypeScript: v5.7.3
  • MySQL8
  • Prisma: v6.5.0
  • JWT
  • bcrypt: v5.1.1
  • Jest: v29.7.0
  • supertest: v7.0.0
  • ESLint: v9.18.0
  • Prettier: v3.4.2
  • pnpm:v10.0.0
  • Redis

3、目录结构

标准、清晰的目录结构方便项目的后期维护。

ruby 复制代码
.
├── prisma/                 # Prisma相关目录
│   ├── migration/          # 数据库迁移脚本
│   │   └── init.sql        # 初始化数据库脚本
│   ├── schema.prisma       # Prisma模型定义
│   └── prisma.service.ts   # Prisma服务
├── src/                    # 源代码目录
│   ├── config/             # 配置文件目录
│   │   ├── database.config.ts  # 数据库配置
│   │   ├── redis.config.ts     # Redis配置
│   │   └── jwt.config.ts       # JWT配置
│   ├── module/             # 功能模块目录
│   │   ├── auth/           # 认证模块
│   │   │   ├── dto/        # 数据传输对象
│   │   │   └── entity/     # 实体
│   │   ├── user/           # 用户模块
│   │   │   ├── dto/
│   │   │   ├── entity/
│   │   │   ├── user.controller.ts
│   │   │   ├── user.service.ts
│   │   │   └── user.module.ts
│   │   └── tool/           # 工具模块
│   │       ├── dto/
│   │       ├── entity/
│   │       ├── tool.controller.ts
│   │       ├── tool.service.ts
│   │       └── tool.module.ts
│   ├── common/             # 通用功能目录
│   │   ├── decorator/      # 自定义装饰器
│   │   ├── filter/         # 异常过滤器
│   │   ├── guard/          # 守卫
│   │   ├── interceptor/    # 拦截器
│   │   ├── middleware/     # 中间件
│   │   └── pipe/           # 管道
│   ├── interface/          # 接口定义目录
│   │   └── index.ts        # 接口导出文件
│   ├── util/               # 工具函数目录
│   │   └── index.ts        # 工具函数导出文件
│   ├── app.module.ts       # 主模块
│   └── main.ts             # 应用入口
├── test/                   # 测试目录
│   ├── app.e2e-spec.ts     # 端到端测试
│   └── jest-e2e.json       # Jest端到端测试配置
├── .gitignore              # Git忽略文件配置
├── .prettierrc             # Prettier格式化配置
├── eslint.config.mjs       # ESLint配置
├── nest-cli.json           # NestJS CLI配置
├── package.json            # 项目依赖和脚本配置
├── tsconfig.json           # TypeScript配置
└── tsconfig.build.json     # TypeScript构建配置

主要目录解释:

  1. prisma:包含数据库相关的配置和迁移文件,使用Prisma作为ORM工具。

  2. src:项目核心代码目录

    • config:存放各种配置文件。
    • module:按照功能划分的模块,采用NestJS的模块化设计
    • common:存放通用的功能组件,如装饰器、过滤器等。
    • interface和util:存放接口定义和工具函数。
  3. test:包含端到端测试文件和Jest配置。

  4. 根目录下的配置文件:包含项目构建、格式化、版本控制等配置。

4、环境配置

项目使用根目录下.env文件管理敏感配置。

配置文件中主要包括:

  • 数据库连接字符串(DATABASE_URL)
  • JWT密钥和过期时间(JWT_SECRET, JWT_EXPIRES_IN)
  • Redis连接配置

5、Prisma数据库配置

schema.prisma文件中主要配置了如下几部分:

  • Client生成器配置
  • 数据源配置
  • 数据模型定义
  • 枚举类型定义
  • 数据库表名映射

5.1、Client生成器配置

ini 复制代码
generator client {
  provider = "prisma-client-js"
}

5.2、数据源配置

ini 复制代码
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

-provider = "mysql":指定使用MySQL作为数据库。 -url = env("DATABASE_URL"):指定数据库连接信息,从环境变量中读取。

5.3、数据模型定义

项目中定义了UserTool两个模型。这里以User模型为例。

kotlin 复制代码
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  password  String
  name      String?
  role      Role     @default(USER)
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  @@map("sys_user")
}
  • 主键id自增。 -email字段唯一。 -role角色使用枚举类型,默认值为USER。 -@@map("sys_user")将模型映射到sys_user数据表。

5.4、枚举类型定义

arduino 复制代码
enum Role {
  USER
  ADMIN
} 

6、用户模块

通过用户模块了解一下一个简单的业务模块包含哪些组成部分。

graph TD G[用户模块
UserModule] -->|注册依赖| A[用户控制器
UserController] G -->|注册依赖| B[用户服务
UserService] G -->|注册依赖| D[数据库服务
PrismaService] G -.->|服务导出| E[用户服务
UserService] %% 增强样式定义 classDef module fill:#4CAF50,stroke:#388E3C,color:white,stroke-width:2px classDef controller fill:#2196F3,stroke:#0b7dda,color:white,stroke-width:2px classDef service fill:#FF9800,stroke:#e68a00,color:white classDef service1 fill:white,stroke:#e68a00,color:#FF9800 classDef database fill:#9C27B0,stroke:#7b1fa2,color:white,stroke-width:2px %% 应用样式 class G module class A controller class B service class D database class E service1

6.1、模块定义

typescript 复制代码
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { PrismaService } from '../../prisma/prisma.service';

@Module({
  controllers: [UserController],
  providers: [UserService, PrismaService],
  exports: [UserService],
})
export class UserModule {} 

在 NestJS 中,模块(Module)是组织应用程序的核心单元,用于封装相关的控制器、服务和其他提供者。

模块通过@Module()装饰器定义,并指定其元数据(如providerscontrollersimportsexports)。

@Module()装饰器接受一个对象,包含以下关键属性:

属性 说明
controllers 注册该模块中使用的控制器(Controller)。
providers 注册该模块中可注入的提供者(如 Service、Repository、Helper 等)。
imports 导入其他模块。
exports 导出该模块的提供者,供其他模块复用。

6.2、控制器

graph TD A[用户控制器
UserController] A -->|使用| B[用户服务
UserService] A -->|使用| D[DTO
CreateUserDto] %% 增强样式定义 classDef controller fill:#2196F3,stroke:#0b7dda,color:white,stroke-width:2px classDef service fill:#FF9800,stroke:#e68a00,color:white classDef dto fill:#9C27B0,stroke:#7b1fa2,color:white,stroke-width:2px %% 应用样式 class A controller class B service class D dto
typescript 复制代码
import { Controller, Get, Post, Body, Param, Delete } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.userService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.userService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.userService.findOne(+id);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.userService.remove(+id);
  }
} 

在 NestJS 中,控制器(Controller) 负责处理传入的 HTTP 请求并返回响应。它通过装饰器(如@Controller()@Get()@Post()等)定义路由和请求处理逻辑。

NestJS 提供了一系列装饰器来处理不同的 HTTP 方法:

装饰器 HTTP 方法 示例用法
@Get() GET @Get('profile')GET /user/profile
@Post() POST @Post()POST /user
@Put() PUT @Put(':id')PUT /user/1
@Delete() DELETE @Delete(':id')DELETE /user/1
@Patch() PATCH @Patch(':id')PATCH /user/1
@Options() OPTIONS @Options()OPTIONS /user
@Head() HEAD @Head()HEAD /user
@All() 所有方法 @All()→ 匹配所有 HTTP 方法

路由参数与请求数据

路径参数(Path Parameters)

使用@Param()获取路由参数:

typescript 复制代码
@Get(':id') // 匹配 GET /user/1
findOne(@Param('id') id: string): string {
  return`This action returns user #${id}`;
}

查询参数(Query Parameters)

使用@Query()获取查询参数:

typescript 复制代码
@Get() // 匹配 GET /user?limit=10
findAll(@Query('limit') limit: number) {
  return`Returning ${limit} users`;
}

请求体(Request Body)

使用@Body()获取请求体数据(需配合 DTO 类):

typescript 复制代码
@Post()
create(@Body() createUserDto: CreateUserDto) {
  return this.userService.create(createUserDto);
}

6.3、Service

typescript 复制代码
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { CreateUserDto } from './dto/create-user.dto';
import { User } from '@prisma/client';
import * as bcrypt from 'bcrypt';

@Injectable()
export class UserService {
  constructor(private prisma: PrismaService) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
    
    return this.prisma.user.create({
      data: {
        ...createUserDto,
        password: hashedPassword,
      },
    });
  }

  async findAll(): Promise<User[]> {
    return this.prisma.user.findMany();
  }

  async findOne(id: number): Promise<User | null> {
    return this.prisma.user.findUnique({
      where: { id },
    });
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.prisma.user.findUnique({
      where: { email },
    });
  }

  async remove(id: number): Promise<User> {
    return this.prisma.user.delete({
      where: { id },
    });
  }
} 
  • 使用 @Injectable() 装饰器标记一个类为可注入的服务。
  • 构造方法注入PrismaService进行数据库操作。

6.4、实体类

typescript 复制代码
import { Role } from '@prisma/client';

export class User {
  id: number;
  email: string;
  password: string;
  name?: string;
  role: Role;
  createdAt: Date;
  updatedAt: Date;
}

作用:导入枚举类型Role,定义实体类User

总结

本篇文章中介绍了在NestJS项目中怎样开发一个基础的业务模块、怎样定义一个控制器类、怎样定义一个Service、使用Prisma进行简单地增删改查操作、怎样定义一个实体类、怎样定义一个DTO类。

相关推荐
小安同学iter7 分钟前
SpringBoot(三)环境隔离/外部化配置/单元测试/可观测性/生命周期
java·spring boot·后端
lamdaxu1 小时前
Java基础 - 注解机制详解
后端
Lemon1251 小时前
DeepSpeed多机多卡微调实验记录
后端
ClarkLee1 小时前
控制反转(IOC)及依赖注入(DI)详解
后端
紧跟先前的步伐1 小时前
【Golang】第十一弹------反射
开发语言·后端·golang
三木SanMu2 小时前
LangChain基础系列之LLM接口详解:从原理到实战的全攻略
后端
失业写写八股文2 小时前
Spring基础:Spring特性与优势
后端·spring
Asthenia04122 小时前
Java 排序深度解析:升序、降序与实现方式的抉择
后端
qq_447663052 小时前
Spring的事务处理
java·后端·spring
bobz9652 小时前
qemu 启动 debian 虚拟机
后端