创建项目
注意:node 18+ 以上
// 安装cli
npm i -g @nestjs/cli
nest new server
npm run start:dev
访问 http://localhost:3000 显示Hello World!启动成功
连接数据库
yarn add @nestjs/mongoose mongoose @nestjs/config @nestjs/mapped-types
根目录新建 config/env.ts
import * as fs from 'fs';
import * as path from 'path';
const isProd = process.env.NODE_ENV === 'production';
function parseEnv() {
const localEnv = path.resolve('.env');
const prodEnv = path.resolve('.env.prod');
if (!fs.existsSync(localEnv) && !fs.existsSync(prodEnv)) {
throw new Error('缺少环境配置文件');
}
const filePath = isProd && fs.existsSync(prodEnv) ? prodEnv : localEnv;
return { path: filePath };
}
export default parseEnv();
根路径新建.env
// 数据库地址
DB_HOST=localhost
// 数据库端口
DB_PORT=27017
// 数据库登录名
// DB_USER=root
// 数据库登录密码
// DB_PASSWORD=123456
// 数据库名字
DB_DATABASE=my-chat-app
// secret
SECRET=my-chat-app
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
+ import { ConfigService, ConfigModule } from '@nestjs/config';
+ import { MongooseModule } from '@nestjs/mongoose';
+ import envConfig from '../config/env';
@Module({
imports: [
+ ConfigModule.forRoot({
+ isGlobal: true,
+ envFilePath: [envConfig.path],
+ }),
+ MongooseModule.forRootAsync({
+ imports: [ConfigModule],
+ inject: [ConfigService],
+ useFactory: async (configService: ConfigService) => ({
+ uri: `mongodb://${configService.get('DB_HOST')}:${configService.get(
+ 'DB_PORT',
+ )}/${configService.get('DB_DATABASE', '')}`,
+ })
+ }),
+ }),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
新增user模块
// 命令行执行会自动创建用户模块
nest g res user
选择REST API 回车
注意:如果有报错生成不出文件则额外执行
yarn add @nestjs/schematics -D
添加参数验证方法
yarn add class-transformer class-validator reflect-metadata uuid
yarn add @types/uuid -D
user/dto/create-user.dto.ts 添加校验规则
+ import { IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDto {
+ @IsNotEmpty({ message: '用户名必填' })
+ @IsString({ message: '用户名必填' })
+ readonly username: string;
+ @IsNotEmpty({ message: '昵称必填' })
+ @IsString({ message: '昵称必填' })
+ readonly name: string;
+ @IsNotEmpty({ message: '密码必填' })
+ @IsString({ message: '密码必填' })
+ readonly password: string;
}
main.ts 开启验证
...
+ import { ValidationPipe } from '@nestjs/common';
...
const app = await NestFactory.create(AppModule);
+ app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
...
user/entities/user.entity.ts 添加用户实体类
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: false,
},
username: {
type: String,
required: true,
unique: true,
},
image: {
type: String,
},
password: {
type: String,
required: [true, '请输入密码'],
minlength: [6, '密码最小长度6个字符'],
},
// followingIds:{
// type: [
// {
// type: mongoose.Schema.Types.ObjectId,
// ref: 'User',
// }
// ],
// default: []
// },
avatar: {
type: String,
},
remark: {
type: String,
default: '',
},
conversationIds: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Conversation',
default: [],
},
],
},
{ timestamps: true },
);
export default userSchema;
export type UserType = {
_id: string;
name: string;
username: string;
password: string;
// followingIds:UserType[]
avatar: string;
image: string;
status: string;
remark: string;
conversationIds: string[];
createdAt: string;
updatedAt: string;
};
user/user.modules.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
+ import { MongooseModule } from '@nestjs/mongoose';;
+ import userSchema from './entities/user.entity';
@Module({
// 导入User实体
+ imports: [MongooseModule.forFeature([{ name: 'Users', schema: userSchema }])],
controllers: [UserController],
providers: [UserService],
// 导出当前的Service 便于其他模块调用
+ exports: [UserService],
})
export class UserModule {}
user/user.service.ts 添加注册方法
...
+ import { InjectModel } from '@nestjs/mongoose';
+ import { Model } from 'mongoose';
+ import { UserType } from './entities/user.entity';
...
@Injectable()
export class UserService {
...
+ constructor(
+ @InjectModel('Users') private readonly userModel: Model<UserType>,
+ ) {}
+ async create(createUserDto: CreateUserDto) {
+ const { username } = createUserDto;
+ const existUser = await this.userModel.findOne({
+ username,
+ });
+ if (existUser) {
+ throw new HttpException('用户名已存在', HttpStatus.BAD_REQUEST);
+ }
+ const newUser = await this.userModel.create(createUserDto);
+ return newUser._id;
+ }
...
测试接口
上文由nest g res user 指令生成模块默认生成多个接口
访问
user POST 创建
user GET 查询 ...
不出意外将会添加成功如下:
接口参数校验可以看到也已经生效
添加全局相应拦截
可以看到请求成功后返回的数据格式并不是我们想要的,所以添加一个全局的相应拦截格式化一下
跟路径新建文件夹 common/interceptor
// 生成拦截器
nest g interceptor common/interceptor/response
app.module.ts 添加生成的拦截器
...
providers: [
AppService,
+ {
+ provide: APP_INTERCEPTOR,
+ useClass: ResponseInterceptor,
+ },
],
// 生成的拦截器文件中添加
common/interceptor/response/response.interceptor.ts
...
+ import { map, Observable } from 'rxjs';
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
...
- return next.handle()
+ return next.handle().pipe(
+ map((data) => ({
+ data,
+ code: 200,
+ status: 200,
+ msg: '操作成功',
+ })),
...
);
}
可以看到请求成功后返回的结果已经处理成功
添加异常相应拦截
请求异常时也需要添加格式化
// 生成拦截器
nest g filter common/interceptor/http-exception
app.module.ts 添加生成的异常拦截器
...
providers: [
AppService,
+ {
+ provide: APP_FILTER,
+ useClass: HttpExceptionFilter,
+ },
{
provide: APP_INTERCEPTOR,
useClass: ResponseInterceptor,
},
...
],
// 生成的拦截器文件中添加
// 这里我使用了lodash处理 exception 数据可以使用其他的库或其他方法处理
common/interceptor/http-exception/http-exception.filter.ts
+ import {ArgumentsHost,Catch,ExceptionFilter,HttpException} from '@nestjs/common';
+ import { Request, Response } from 'express';
+ import { get } from 'lodash';
...
catch(exception: HttpException, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const request = ctx.getRequest<Request>();
+ const response = ctx.getResponse<Response>();
+ const status = exception.getStatus();
+ const data = exception.getResponse();
+ if (status === 400 && typeof data === 'object') {
+ const msgs = get(data, 'message', exception.message);
+ const msg = Array.isArray(msgs) ? msgs.join(',') : msgs;
+ response.status(200).json({
+ code: status,
+ msg,
+ });
+ } else {
+ response.status(200).json({
+ code: status,
+ msg: data,
+ });
}
....
未完待续...
下一章将继续完善基础模块 ^-^