# nest-prisma-template **Repository Path**: Kanbara-Take/nest-prisma-template ## Basic Information - **Project Name**: nest-prisma-template - **Description**: nest-prisma-mysql-redis-minio-swagger 后端单体项目模板 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2024-08-20 - **Last Updated**: 2024-08-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
## 项目概述 `node版本 18.20.0` 技术栈: - Nest 后端框架 - Prisma ORM 连接数据库框架 - mysql 持久化存储 - redis 缓存以及临时数据存储 - minio OSS对象存储 - swagger swagger文档 - winston 日志 功能点 - jwt 单 token 登录注册无感刷新 - svg-captcha 图形验证码 - email 邮箱验证码 - 动态读取环境配置 - 多版本共存开发 ## 项目使用 ```bash # 下载依赖 $ npm install # 开发环境 $ npm run start:dev # 生成环境 $ npm run build ``` ## 项目初始化 ### 1.下载nest脚手架 ```BASH npm install -g @nestjs/cli ``` ### 2.创建项目 ```BASH nest new 项目名 ``` ### 3.解决 ESLint 的 Delete `CR` 报错 .eslintrc.js ```JS module.exports = { .... rules: { .... "prettier/prettier": ["error", { endOfLine: "auto" }], }, }; ``` ### 4.关闭自动生成测试模块 .nest-cli.json ```JSON "generateOptions": { "spec": false }, ``` ### 5. user 用户模块(模块例子) ``` bash # 生成模块(不生成CRUD) nest g resource user ``` /views/user/user.controller.ts ``` js import { Controller, Post, Body } from '@nestjs/common'; import { UserService } from './user.service'; import { RegisterUserDto } from './dto/register-user.dto'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Post('register') async register(@Body() registerUser: RegisterUserDto) { return await this.userService.create(registerUser); } } ``` /views/user/user.service.ts ``` js import { Inject, Injectable } from '@nestjs/common'; import { PrismaService } from 'src/middleware/prisma/prisma.service'; import { Prisma } from '@prisma/client'; @Injectable() export class UserService { @Inject(PrismaService) private prisma: PrismaService; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore async create(data: Prisma.UserCreateInput) { // 校验验证码 delete data.captcha; const list = data; // 自动生成账号 唯一id const table = await this.prisma.user.findMany(); list.account = `HH${(table.length + 1).toString().padStart(5, '0')}`; // 自动生成默认账号头像 list.avatarURL = 'http://localhost:9000/dev/err.png'; await this.prisma.user.create({ data: list, select: { id: true, }, }); return '新增成功'; } } ``` /views/user/dto/register-user.dto.ts ``` js export class RegisterUserDto { username: string; nickName: string; password: string; email: string; phone: string; captcha: string; } ``` ## 项目开发 ### 1. swagger 配置 ``` bash npm install --save @nestjs/swagger ``` ```js import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; async function bootstrap() { ... const config = new DocumentBuilder() .setTitle('Nest-template') .setDescription('Nest.js 后端项目模板') .setVersion('V1.0.0') .addBearerAuth({ type: 'http', description: '基于 jwt 的认证', }) .build(); const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('swagger-doc', app, document); ... } bootstrap(); ``` `注释实例` 注册接口 /views/user/user.controller.ts ```JS import { Controller, Post, Body, HttpStatus } from '@nestjs/common'; import { UserService } from './user.service'; import { RegisterUserDto } from './dto/register-user.dto'; import { ApiOperation, ApiTags, ApiResponse,ApiQuery,ApiBearerAuth } from '@nestjs/swagger'; import { PublicVoSuccess, PublicVoError } from 'src/utils/publicVo/index'; @ApiTags('用户管理') // 模块名描述 @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Post('register') // 接口描述 @ApiOperation({ summary: '用户注册', description: '用户注册接口' }) // jwt 配置 描述 @ApiBearerAuth() // query 请求体 接口描述 @ApiQuery({ name: 'refreshToken', type: String, description: '刷新token', required: true, }) // 公共 返回体 接口描述 VO @ApiResponse({ status: HttpStatus.OK, description: '请求成功', type: PublicVoSuccess, }) @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: '请求失败', type: PublicVoError, }) async register(@Body() registerUser: RegisterUserDto) { return await this.userService.create(registerUser); } } ``` 注册 请求体 body 接口描述 dto /views/user/dto/register-user.dto.ts ```JS import { ApiProperty } from '@nestjs/swagger'; export class RegisterUserDto { // body 请求体 描述 @ApiProperty({ description: '用户名', required: true }) username: string; @ApiProperty({ description: '密码', required: true }) password: string; @ApiProperty({ description: '昵称', required: false }) nickName: string; @ApiProperty({ description: '邮箱', required: false }) email: string; @ApiProperty({ description: '电话', required: false }) phone: string; @ApiProperty({ description: '验证码', required: true }) captcha: string; } ``` 公共 返回体 接口描述 VO /utils/publicVo/index.ts ```JS import { ApiProperty } from '@nestjs/swagger'; export class PublicVoSuccess { @ApiProperty({ example: '200', description: '状态码', required: false }) code: number; @ApiProperty({ example: '请求成功', description: '请求消息', required: false, }) msg: string; @ApiProperty({ example: 'success', description: '数据', required: false }) data: string; } export class PublicVoError { @ApiProperty({ example: '500', description: '状态码', required: false }) code: number; @ApiProperty({ example: '请求失败', description: '请求消息', required: false, }) msg: string; @ApiProperty({ example: 'error', description: '数据', required: false }) data: string; } ``` ### 2. env动态读取环境配置 ``` bash npm install --save @nestjs/config ``` /app.module.ts ```JS import { ConfigModule } from '@nestjs/config'; @Module({ imports: [ ... ConfigModule.forRoot({ isGlobal: true, envFilePath: 'src/.env', }), ], ... }) export class AppModule {} ``` .nest-cli.json /main.ts `动态读取项目运行端口号配置` ```JS import { ConfigService } from '@nestjs/config'; async function bootstrap() { ... const configService = app.get(ConfigService); ... await app.listen(configService.get('nest_server_port')); } bootstrap(); ``` `动态 读取 redis 配置 方式一` /middleware/redis/redis.module.ts ```JS import { ConfigService } from '@nestjs/config'; @Global() @Module({ providers: [ { ... async useFactory(configService: ConfigService) { const client = createClient({ socket: { host: configService.get('redis_server_host'), port: configService.get('redis_server_port'), }, database: configService.get('redis_server_db'), }); ... }, inject: [ConfigService], }, ], ... }) export class RedisModule {} ``` `动态 读取 email配置 方式二` /middleware/email/email.service.ts ```JS ... import { ConfigService } from '@nestjs/config'; @Injectable() export class EmailService { ... constructor(private configService: ConfigService) { this.transporter = createTransport({ host: this.configService.get('nodemailer_host'), port: this.configService.get('nodemailer_port'), secure: false, auth: { user: this.configService.get('nodemailer_auth_user'), pass: this.configService.get('nodemailer_auth_pass'), }, }); } ... } ``` .package.json `打包时将.env文件复制到dist文件里` ```JS { ... "scripts": { ... "build": "nest build && cp .env dist/", ... }, ... } ``` ### 3. 封装 prisma 为中间件 `安装 prisma` ``` bash npm install prisma --save-dev ``` `prisma 初始化创建 schema` ``` bash npx prisma init ``` `prisma 更改配置` /prisma ``` bash DATABASE_URL="mysql://root:你的密码@localhost:3306/数据库名" ``` .env ``` bash datasource db { provider = "mysql" url = env("DATABASE_URL") } ``` ``` bash # 重置数据库 npx prisma migrate reset # 创建表 npx prisma migrate dev --name 表名 ``` `封装 prisma 为中间件` ``` bash # 生成模块 nest g service prisma nest g module prisma ``` /middleware/prisma/prisma.module.ts ```js import { Global, Module } from '@nestjs/common'; import { PrismaService } from './prisma.service'; @Global() @Module({ providers: [PrismaService], exports: [PrismaService], }) export class PrismaModule {} ``` /middleware/prisma/prisma.service.ts ``` js import { Injectable, OnModuleInit } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; @Injectable() export class PrismaService extends PrismaClient implements OnModuleInit { constructor() { super({ log: [ { emit: 'stdout', level: 'query' } ] }) } async onModuleInit() { await this.$connect(); } } ``` ### 4. 封装 redis 为中间件 ``` bash # 生成模块 nest g module redis nest g service redis ``` ``` bash npm install --save redis ``` /middleware/redis/redis.module.ts ```JS import { Global, Module } from '@nestjs/common'; import { RedisService } from './redis.service'; import { createClient } from 'redis'; @Global() @Module({ providers: [ RedisService, { provide: 'REDIS_CLIENT', async useFactory() { const client = createClient({ socket: { host: 'localhost', port: 6379, }, database: 2, }); await client.connect(); return client; }, }, ], exports: [RedisService], }) export class RedisModule {} ``` /middleware/redis/redis.service.ts ```JS import { Injectable, Inject } from '@nestjs/common'; import { RedisClientType } from 'redis'; @Injectable() export class RedisService { @Inject('REDIS_CLIENT') private redisClient: RedisClientType; async get(key: string) { return await this.redisClient.get(key); } async set(key: string, value: string | number, ttl?: number) { await this.redisClient.set(key, value); if (ttl) { await this.redisClient.expire(key, ttl); } } async del(key: string) { await this.redisClient.del(key); } } ``` ### 5. jwt 配置 ``` bash # 生成模块 nest g module jwt nest g service jwt ``` ``` bash npm install --save @nestjs/jwt --save ``` /middleware/jwt/jwt.module.ts ```JS import { Global, Module } from '@nestjs/common'; import { JwtService } from './jwt.service'; import { JwtModule } from '@nestjs/jwt'; @Global() @Module({ imports: [ JwtModule.registerAsync({ global: true, useFactory() { return { secret: 'HH', signOptions: { expiresIn: '30m', // 默认 30 分钟 }, }; }, }), ], providers: [JwtService], exports: [JwtService], }) export class JwtsModule {} ``` /views/user/user.service.ts `userLogin`方法 ```JS ... import { JwtService } from '@nestjs/jwt'; export class UserService { @Inject(JwtService) private jwtService: JwtService; ... async login() { ... return { foundUser, token: this.jwtService.sign( { userId: foundUser.id, username: foundUser.username, }, { expiresIn: '7d', }, ), }; } } ``` ### 6. 登录鉴权 ``` bash # 生成模块 nest g guard auth --flat ``` /middleware/auth/auth.guard.ts ```JS import { CanActivate, ExecutionContext, Inject, Injectable, UnauthorizedException, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; import { Request, Response } from 'express'; import { Observable } from 'rxjs'; interface JwtUserData { userId: number; username: string; } declare module 'express' { interface Request { user: JwtUserData; } } @Injectable() export class AuthGuard implements CanActivate { @Inject() private reflector: Reflector; @Inject(JwtService) private jwtService: JwtService; canActivate( context: ExecutionContext, ): boolean | Promise