nestJS基本概念
-
模块(Modules)
- 模块是用来组织代码的基本单位。
- 每个应用至少有一个根模块
AppModule
。
- 模块通过
@Module()
装饰器定义。
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UserService],
controllers: [UserController],
exports: [UserService],
})
export class UserModule {}
-
控制器(Controllers)
- 控制器负责处理传入的请求,并返回响应。
- 使用
@Controller()
装饰器定义路由路径。
- 通过装饰器定义路由处理方法(如
@Get()
, @Post()
等)。
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Get('query')
findAll(): string {
return this.catsService.findAll();
}
@Post('add')
create(@Body() createCatDto: CreateCatDto): string {
return this.catsService.create(createCatDto);
}
}
-
服务(Providers)
- 服务用于处理业务逻辑,通常是注入到控制器或其他服务中。
- 使用
@Injectable()
装饰器标记为可注入的服务。
import { Injectable, HttpException, HttpStatus, Inject } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
@Inject(JwtService)
private jwtService: JwtService;
async createUser(username: string, password: string): Promise<User> {
const hashedPassword = await bcrypt.hash(password, 10);
const user = this.userRepository.create({
username,
password: hashedPassword,
});
return this.userRepository.save(user);
}
async findByUsername(username: string): Promise<User> {
return this.userRepository.findOne({ where: { username } });
}
async findById(id: number): Promise<User> {
return this.userRepository.findOne({ where: { id } });
}
async updatePassword(id: number, newPassword: string): Promise<void> {
const hashedPassword = await bcrypt.hash(newPassword, 10);
await this.userRepository.update(id, { password: hashedPassword });
}
}
-
中间件(Middleware)
- 中间件是在路由处理程序之前调用的函数。
- 使用
@Middleware()
装饰器定义。
- 在模块中应用中间件
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
@Module({
imports: [],
controllers: [],
providers: [],
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
-
拦截器(Interceptors)
- 拦截器用于在处理请求前后添加额外的逻辑,如日志记录、缓存等。
- 使用
@Injectable()
装饰器定义。
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import { Observable, map } from 'rxjs';
interface Data<T> {
data: T;
}
@Injectable()
export class SuccessResponse<T> implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<Data<T>> {
return next.handle().pipe(
map((data) => {
return {
data,
code: 1,
message: '请求成功',
success: true,
};
}),
);
}
}
-
管道(Pipes)
- 管道用于转换和验证请求数据。
- 内置的管道包括
ValidationPipe
、ParseIntPipe
等。
import { Controller, Get, Param, ParseIntPipe } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return `Cat ${id}`;
}
}
-
守卫(Guards)
- 守卫用于定义请求是否应该被处理的逻辑。
- 使用
@Injectable()
装饰器定义。
import { JwtService } from '@nestjs/jwt';
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { Request } from 'express';
import { Observable } from 'rxjs';
@Injectable()
export class LoginGuard implements CanActivate {
@Inject(JwtService)
private jwtService: JwtService;
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request: Request = context.switchToHttp().getRequest();
const token = request.header('authtoken') || '';
try {
const info = this.jwtService.verify(token);
(request as any).user = info.user;
return true;
} catch (e) {
throw new UnauthorizedException('登录已失效,请重新登录');
}
}
}
-
异常过滤器(Exception Filters)
- 异常过滤器用于处理和转换异常响应。
- 使用
@Catch()
装饰器定义。
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
} from '@nestjs/common';
import { Response, Request } from 'express';
@Catch()
export class HttpFaild implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
console.log("🚀 ~ HttpFaild ~ exception:", exception)
const ctx = host.switchToHttp();
const request = ctx.getRequest<Request>();
const response = ctx.getResponse<Response>();
const status = exception?.getStatus();
console.log(status, 'status');
response.status(status).json({
success: false,
time: new Date(),
msg: exception.message,
code: status,
path: request.url,
});
}
}
常用功能
-
数据库集成(TypeORM)
- 使用
@nestjs/typeorm
模块集成 TypeORM。
- 配置数据库连接并定义实体。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { MenuModule } from './menu/menu.module';
import { PostModule } from './post/post.module';
import { CrawlerService } from './crawler/crawler.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ScheduleModule } from '@nestjs/schedule';
import { JwtModule } from '@nestjs/jwt';
import { Menu } from './menu/menu.entity';
@Module({
imports: [
ScheduleModule.forRoot(),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'MuMu5217426',
database: 'min-app',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
charset: 'utf8mb4',
timezone: '+08:00',
}),
TypeOrmModule.forFeature([Menu]),
JwtModule.register({
global: true,
secret: 'syb-secret',
signOptions: {
expiresIn: '7d',
},
}),
UserModule,
MenuModule,
PostModule,
],
controllers: [AppController],
providers: [AppService, CrawlerService],
})
export class AppModule {}
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
OneToMany,
} from 'typeorm';
import { Exclude } from 'class-transformer';
import { Post } from '../post/post.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
username: string;
@Column()
@Exclude({ toPlainOnly: true })
password: string;
@OneToOne(() => User, { nullable: true })
@JoinColumn()
relatedUser: User;
@Column({ nullable: true })
relatedUserId: number;
@OneToMany(() => Post, (post) => post.user)
posts: Post[];
}
-
身份验证(Passport.js)
- 使用
@nestjs/passport
模块集成 Passport.js。
- 配置 JWT 策略。
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
@Module({
imports: [
PassportModule,
JwtModule.register({
secret: 'secretKey',
signOptions: { expiresIn: '7d' },
}),
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
NestJS 中与 MySQL 交互
-
安装依赖:
@nestjs/typeorm
typeorm
mysql2
-
配置数据库连接
- 在
AppModule
中使用 TypeOrmModule.forRoot
方法配置数据库连接。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { MenuModule } from './menu/menu.module';
import { PostModule } from './post/post.module';
import { CrawlerService } from './crawler/crawler.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ScheduleModule } from '@nestjs/schedule';
import { JwtModule } from '@nestjs/jwt';
import { Menu } from './menu/menu.entity';
@Module({
imports: [
ScheduleModule.forRoot(),
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'MuMu5217426',
database: 'min-app',
entities: ['dist/**/*.entity{.ts,.js}'],
synchronize: true,
charset: 'utf8mb4',
timezone: '+08:00',
}),
TypeOrmModule.forFeature([Menu]),
JwtModule.register({
global: true,
secret: 'syb-secret',
signOptions: {
expiresIn: '7d',
},
}),
UserModule,
MenuModule,
PostModule,
],
controllers: [AppController],
providers: [AppService, CrawlerService],
})
export class AppModule {}
-
创建实体
- 使用
@Entity()
装饰器定义实体类。
- 使用
@PrimaryGeneratedColumn()
, @Column()
, 等装饰器定义实体字段。
import {
Entity,
PrimaryGeneratedColumn,
Column,
OneToOne,
JoinColumn,
OneToMany,
} from 'typeorm';
import { Exclude } from 'class-transformer';
import { Post } from '../post/post.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
username: string;
@Column()
@Exclude({ toPlainOnly: true })
password: string;
@OneToOne(() => User, { nullable: true })
@JoinColumn()
relatedUser: User;
@Column({ nullable: true })
relatedUserId: number;
@OneToMany(() => Post, (post) => post.user)
posts: Post[];
}
-
使用 Repository 进行数据库操作
- 使用
@InjectRepository
注入实体的 Repository。
- 使用
repository
的方法如 find
, findOne
, save
, delete
等进行数据库操作。
import { Injectable, Inject } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { CreateMenuDto } from './dto/create-menu.dto';
import { UpdateMenuDto } from './dto/update-menu.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Menu } from './menu.entity';
@Injectable()
export class MenuService {
constructor(
@InjectRepository(Menu)
private menuRepository: Repository<Menu>,
) {}
@Inject(JwtService)
async create(createMenuDto: CreateMenuDto) {
return await this.menuRepository.save({
...createMenuDto,
});
}
async update(id: number, updateMenuDto: UpdateMenuDto) {
const qb = await this.menuRepository.createQueryBuilder();
return await qb.update().set(updateMenuDto).where({ id }).execute();
}
async findAll() {
return await this.menuRepository.find();
}
async findOne(id: number) {
return await this.menuRepository.find({
where: { id },
});
}
async findByCategory(category: number) {
return await this.menuRepository.find({
where: { category },
});
}
async remove(id: number) {
const qb = await this.menuRepository.createQueryBuilder();
return await qb.delete().where({ id }).execute();
}
}