本文详细介绍了如何使用Nest项目实战,从安装和创建第一个Nest项目开始,逐步引导读者了解路由与控制器、服务与模块以及数据库集成等内容。文章还涵盖了中间件与装饰器的使用,并提供了项目部署与测试的指导。
Nest.js 概述 Nest.js 简介Nest.js 是一个基于 TypeScript 的 Web 框架,它使用现代的 JavaScript 和 Node.js 生态系统。Nest.js 的设计理念是构建可维护、可扩展和强大的服务器端应用程序。它结合了面向对象编程(OOP)和面向切面编程(AOP)的思想,并利用了 TypeScript 的静态类型检查,使得开发人员能够更加高效地编写代码。
Nest.js 旨在提供一个结构化、模块化的开发环境,它允许开发人员以更加清晰和组织化的形式来构建应用程序。Nest.js 使用依赖注入(Dependency Injection, DI)来管理应用程序中的依赖关系,并通过装饰器(Decorators)来增强类型安全和代码可读性。
Nest.js 的特点和优势- 模块化架构:Nest.js 使用模块化架构来组织代码,每个模块可以独立地进行开发、测试和部署。这种结构使得代码保持清晰和易于维护。
- 依赖注入:依赖注入允许你将模块之间的依赖关系以一种清晰、可维护的方式管理,这使得代码更加松耦合,可扩展性更强。
- 类型安全:使用 TypeScript 进行开发,使得代码具有静态类型检查,提高代码的健壮性和可维护性。
- 高性能:Nest.js 采用 Express.js 作为其底层实现,具有很高的性能。
- 装饰器支持:装饰器是在 Nest.js 中广泛使用的一种特性,它使得代码更加简洁和易读。
-
全局安装 Nest CLI:
npm install -g @nestjs/cli
-
创建一个新的 Nest.js 项目:
nest new my-nest-app
这个命令会创建一个新的空项目,并初始化项目文件夹和一些基本的文件。
-
进入项目目录并安装依赖:
cd my-nest-app npm install
- 运行项目:
npm run start
创建一个简单的 Nest.js 应用程序,首先需要创建一个控制器(Controller),一个控制器定义了应用程序的路由(Routes)和处理这些路由的逻辑(Handlers)。
-
创建一个控制器:
nest generate controller hello
-
编辑
hello.controller.ts
文件:import { Controller, Get } from '@nestjs/common'; @Controller('hello') export class HelloController { @Get() getHello(): string { return 'Hello World!'; } }
-
在
app.module.ts
文件中注册控制器:import { Module } from '@nestjs/common'; import { HelloController } from './hello/hello.controller'; @Module({ imports: [], controllers: [HelloController], providers: [], }) export class AppModule {}
- 启动应用程序:
npm run start
访问 http://localhost:3000/hello
即可看到返回的 "Hello World!"。
路由概念
在 Nest.js 中,路由的概念与 Express.js 类似。路由定义了应用程序如何响应客户端发送的 HTTP 请求。路由通常由一个控制器来处理,控制器中定义了处理这些路由的逻辑。
创建控制器
Nest.js 中的控制器可以使用 @Controller
装饰器来定义,并通过 @Get
、@Post
、@Put
和 @Delete
等装饰器来定义具体的路由。
-
创建一个新的控制器:
nest generate controller user
-
编辑
user.controller.ts
文件:import { Controller, Get, Post, Body } from '@nestjs/common'; @Controller('user') export class UserController { @Get() getUsers(): string { return 'List of all users'; } @Post() createUser(@Body() user): string { return `User ${user.name} has been created`; } }
路由参数与依赖注入
路由参数
Nest.js 支持在路由中使用参数。参数可以通过 @Param
装饰器来获取。
-
编辑
user.controller.ts
文件:import { Controller, Get, Param } from '@nestjs/common'; @Controller('user') export class UserController { @Get(':id') getUserById(@Param('id') id: string): string { return `User with ID ${id} has been fetched`; } }
- 运行应用程序:
访问http://localhost:3000/user/1
或者其他 id,将会返回用户信息。
依赖注入
依赖注入是 Nest.js 中非常重要的一个概念,它使得组件之间的解耦更加容易,便于测试和维护。
-
创建一个服务:
nest generate service user
-
编辑
user.service.ts
文件:import { Injectable } from '@nestjs/common'; @Injectable() export class UserService { getUsers(): string { return 'List of all users'; } }
-
在控制器中注入服务:
import { Controller, Get, Param, Injectable, Inject } from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Get() getUsers(): string { return this.userService.getUsers(); } }
模块的概念
在 Nest.js 中,模块是用于组织和分组代码的一个高级概念。每个模块可以包含控制器、服务、提供者(Providers)和模块内的其他依赖关系。模块也可以依赖于其他模块,从而形成一个更加结构化的应用体系。
-
创建一个模块:
nest generate module users
-
编辑
users.module.ts
文件:import { Module } from '@nestjs/common'; import { UserController } from './user.controller'; import { UserService } from './user.service'; @Module({ imports: [], controllers: [UserController], providers: [UserService], exports: [UserService], }) export class UsersModule {}
创建服务
服务是 Nest.js 中主要的业务逻辑处理单元。服务通常包含处理数据库操作、业务逻辑等。
-
创建一个服务:
nest generate service user
-
编辑
user.service.ts
文件:import { Injectable } from '@nestjs/common'; @Injectable() export class UserService { getUsers(): string { return 'List of all users'; } }
服务与控制器的交互
控制器和服务之间的交互是通过依赖注入来实现的。控制器可以注入服务,并通过服务提供的方法来执行相应的业务逻辑。
-
编辑
user.controller.ts
文件:import { Controller, Get, Param, Injectable, Inject } from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Get() getUsers(): string { return this.userService.getUsers(); } }
数据库连接设置
Nest.js 支持多种数据库,包括 MySQL、Postgres、MongoDB 等。这里以 MongoDB 为例。
-
安装 MongoDB 客户端库:
npm install @nestjs/mongoose mongoose
-
创建数据库模块:
nest generate module database
-
编辑
database.module.ts
文件:import { Module } from '@nestjs/common'; import { MongooseModule } from '@nestjs/mongoose'; @Module({ imports: [ MongooseModule.forRoot('mongodb://localhost:27017/mydatabase'), ], }) export class DatabaseModule {}
-
将数据库模块导入到主模块:
import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { DatabaseModule } from './database/database.module'; @Module({ imports: [DatabaseModule], controllers: [AppController], providers: [AppService], }) export class AppModule {}
操作数据库的基本方法
在 Nest.js 中,可以使用 Mongoose 模型来定义数据库中的集合(collections)。
-
定义 Mongoose 模型:
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; import { Document } from 'mongoose'; export type UserDocument = User & Document; @Schema() export class User { @Prop() name: string; @Prop() email: string; } export const UserSchema = SchemaFactory.createForClass(User);
-
创建用户服务:
import { Injectable } from '@nestjs/common'; import { Model } from 'mongoose'; import { User, UserDocument } from './user.schema'; import { InjectModel } from '@nestjs/mongoose'; @Injectable() export class UserService { constructor(@InjectModel(User.name) private userModel: Model<UserDocument>) {} async addUser(user: User): Promise<UserDocument> { const newUser = new this.userModel(user); return newUser.save(); } async getUsers(): Promise<UserDocument[]> { return this.userModel.find().exec(); } async updateUser(id: string, user: User): Promise<UserDocument> { return this.userModel.findOneAndUpdate({ _id: id }, user, { new: true }).exec(); } async deleteUser(id: string): Promise<UserDocument> { return this.userModel.findByIdAndRemove(id).exec(); } }
-
在控制器中调用服务:
import { Controller, Get, Post, Body, Put, Param } from '@nestjs/common'; import { UserService } from './user.service'; @Controller('user') export class UserController { constructor(private readonly userService: UserService) {} @Get() getUsers(): Promise<UserDocument[]> { return this.userService.getUsers(); } @Post() addUser(@Body() user: User): Promise<UserDocument> { return this.userService.addUser(user); } @Put(':id') updateUser(@Param('id') id: string, @Body() user: User): Promise<UserDocument> { return this.userService.updateUser(id, user); } @Delete(':id') deleteUser(@Param('id') id: string): Promise<UserDocument> { return this.userService.deleteUser(id); } }
-
创建数据:
@Post() addUser(@Body() user: User): Promise<UserDocument> { return this.userService.addUser(user); }
-
读取数据:
@Get() getUsers(): Promise<UserDocument[]> { return this.userService.getUsers(); }
-
更新数据:
@Put(':id') updateUser(@Param('id') id: string, @Body() user: User): Promise<UserDocument> { return this.userService.updateUser(id, user); }
- 删除数据:
@Delete(':id') deleteUser(@Param('id') id: string): Promise<UserDocument> { return this.userService.deleteUser(id); }
中间件的使用
在 Nest.js 中,中间件可以在请求到达控制器之前进行处理。中间件可以读取请求、响应,还可以执行一些额外的操作,比如身份验证、日志记录等。
-
创建中间件:
nest generate class logger
-
编辑
logger.ts
文件: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...', req.method, req.url); next(); } }
3..
import { Module } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({
providers: [],
controllers: [],
middlewares: [LoggerMiddleware],
})
export class AppModule {}
-
在控制器中使用中间件:
import { Controller, Get, Post, Body, UseInterceptors, UsePipes, ValidationPipe, UseGuards, UseMiddleware } from '@nestjs/common'; import { LoggerMiddleware } from './logger.middleware'; @Controller('user') export class UserController { @UseMiddleware(LoggerMiddleware) @Get() getUsers(): string { return 'List of all users'; } }
装饰器的使用
Nest.js 中的装饰器可以用于定义路由、控制 HTTP 请求和响应、处理依赖关系等。装饰器使代码更加简洁和易读。
-
定义一个装饰器:
import { SetMetadata } from '@nestjs/common'; export const Public = () => SetMetadata('isPublic', true);
-
在控制器中使用装饰器:
import { Controller, Get, Post, Body, UsePipes, ValidationPipe, UseGuards, Public } from '@nestjs/common'; @Controller('user') export class UserController { @Get() @Public() getUsers(): string { return 'List of all users'; } }
-
在服务中使用装饰器:
import { Injectable, Inject } from '@nestjs/common'; @Injectable() export class UserService { @Inject() private readonly logger: LoggerService; getUsers(): string { this.logger.log('Fetching users...'); return 'List of all users'; } }
实现日志记录
-
创建一个日志服务:
nest generate service logger
-
编辑
logger.service.ts
文件:import { Injectable } from '@nestjs/common'; @Injectable() export class LoggerService { log(message: string): void { console.log('Logger:', message); } }
-
在服务中使用日志服务:
import { Injectable } from '@nestjs/common'; import { LoggerService } from './logger.service'; @Injectable() export class UserService { constructor(private readonly logger: LoggerService) {} getUsers(): string { this.logger.log('Fetching users...'); return 'List of all users'; } }
实现权限控制
-
创建一个权限管理服务:
nest generate service auth
-
编辑
auth.service.ts
文件:import { Injectable } from '@nestjs/common'; @Injectable() export class AuthService { checkPermission(req): boolean { // 实现权限检查逻辑 return true; } }
-
创建一个权限控制中间件:
nest generate class auth-middleware
-
编辑
auth.middleware.ts
文件:import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { AuthService } from './auth.service'; @Injectable() export class AuthMiddleware implements NestMiddleware { constructor(private readonly authService: AuthService) {} use(req: Request, res: Response, next: NextFunction) { if (this.authService.checkPermission(req)) { next(); } else { res.status = 403; res.send('Forbidden'); } } }
-
在控制器中使用权限中间件:
import { Controller, Get, Post, Body, UsePipes, ValidationPipe, UseGuards, UseMiddleware } from '@nestjs/common'; import { AuthMiddleware } from './auth.middleware'; @Controller('user') export class UserController { @UseMiddleware(AuthMiddleware) @Get() getUsers(): string { return 'List of all users'; } }
项目打包与部署
-
打包项目:
npm run build
- 部署到生产环境:
打包后的项目将生成在dist
文件夹中,可以将dist
文件夹中的内容部署到生产环境中。
单元测试与集成测试
在 Nest.js 中,可以使用 Jest 和 Supertest 进行单元测试和集成测试。
-
安装测试依赖:
npm install --save-dev @types/jest @types/supertest jest ts-jest supertest
-
创建测试文件:
nest generate service user-tests
-
编辑
user.service.spec.ts
文件:import { Test, TestingModule } from '@nestjs/testing'; import { UserService } from './user.service'; describe('UserService', () => { let service: UserService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [UserService], }).compile(); service = module.get<UserService>(UserService); }); it('should be defined', () => { expect(service).toBeDefined(); }); it('should get users', async () => { const result = await service.getUsers(); expect(result).toBe('List of all users'); }); });
- 运行测试:
npm run test
项目优化与维护建议
- 代码审查:定期进行代码审查,确保代码质量。
- 持续集成/持续部署(CI/CD):使用 CI/CD 工具自动化测试和部署过程。
- 性能优化:使用工具进行性能监控和分析,优化数据库查询和缓存策略。
- 文档编写:编写详细的技术文档,包括 API 文档和开发指南。
- 代码重构:定期进行代码重构,保持代码的清晰和可维护性。
通过上述步骤,你可以创建一个健壮、可维护的 Nest.js 应用程序,并且可以通过测试、部署和优化来确保其长期稳定运行。