Nest是一个用于构建高效、可扩展的服务器端JavaScript应用程序的框架,它基于现代JavaScript框架Tsyrph,核心特性包括模块化、依赖注入、装饰器等。Nest后端开发提供了强大的工具和设计模式,帮助开发者构建高效且高度可测试的应用程序。通过集成数据库、错误处理和日志记录等功能,Nest框架支持更复杂的功能和特性。
Nest框架简介 Nest框架的基本概念Nest是一个用于构建高效、可扩展的服务器端JavaScript应用程序的框架。Nest框架基于Angular开发团队设计的现代JavaScript框架Tsyrph,它将许多先进的设计原则注入到Node.js中。Nest框架的核心特性包括模块化、依赖注入、装饰器、可插拔适配器等,它允许开发者使用TypeScript或JavaScript开发高效且高度可测试的应用程序。
Nest框架的主要特点有:
- 模块化:Nest使用模块化的方式组织代码,每个模块可以独立开发、测试和部署。模块化有助于代码的复用和维护。
- 依赖注入:Nest框架内置了依赖注入的功能,可以轻松创建和管理依赖关系,使得代码更加解耦和可测试。
- 装饰器:Nest框架使用装饰器来定义控制器、服务、管道、拦截器等,使得代码更加简洁和易读。
- 可插拔适配器:Nest框架可以轻松地与其他库和框架集成,如Express、GraphQL、TypeORM等。
Nest框架的优势在于它可以为开发者提供强大的工具和设计模式,帮助他们构建高效、可维护和可扩展的Web应用程序。以下是Nest框架的一些优势:
- 高效和可测试性:Nest框架使用TypeScript,提供了静态类型检查,使得代码更易于维护和调试。依赖注入使得单元测试更加简单。
- 模块化和可复用性:Nest框架使用模块化设计,使得代码可复用性高,可以方便地在多个项目中共享代码。
- 跨平台和跨库支持:Nest框架可以轻松地集成不同的库和框架,支持多种数据库、消息队列、缓存等。
- 社区支持:Nest框架有一个活跃的社区,提供了大量的插件、模块和示例项目,可以帮助开发者快速上手。
以下是Nest框架的一些具体代码示例来展示其优势:
import { Injectable } from '@nestjs/common';
@Injectable()
export class MyService {
getData(): string {
return 'Some data';
}
}
import { Controller, Get } from '@nestjs/common';
import { MyService } from './my-service';
@Controller('my-service')
export class MyController {
constructor(private readonly myService: MyService) {}
@Get()
getHello(): string {
return this.myService.getData();
}
}
第一个Nest应用
创建第一个Nest项目
要创建第一个Nest项目,可以使用Nest CLI工具。根据Nest CLI的全局安装步骤,执行以下命令:
nest new my-nest-app
上述命令会创建一个名为my-nest-app
的新项目,并初始化所需文件和目录。项目创建完成后,会生成以下目录结构:
my-nest-app
├── src
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── main.ts
│ └── ...
├── node_modules
├── nest-cli.json
├── package.json
├── tsconfig.json
└── ...
项目结构解析
Nest项目的目录结构如下:
- src:源代码目录,包含应用程序的主要代码。
- app.controller.ts:应用程序的入口点。
thritis - app.module.ts:应用程序的根模块。
- main.ts:应用程序的启动文件。
- app.controller.ts:应用程序的入口点。
- node_modules:存放项目依赖的目录。
- nest-cli.json:Nest CLI的配置文件。
- package.json:项目依赖和脚本的配置文件。
- tsconfig.json:TypeScript编译器的配置文件。
app.controller.ts
控制器是处理HTTP请求和响应的组件。默认的app.controller.ts
文件如下:
import { Controller, Get, HttpException, HttpStatus } from '@nestjs/common';
@Controller('app')
export class AppController {
@Get()
getHello(): string {
return 'Hello World!';
}
}
在这个文件中,@Controller('app')
装饰器定义了一个名为app
的控制器。@Get()
装饰器定义了一个GET请求路由,返回一个字符串'Hello World!'
。
app.module.ts
模块是Nest框架的组织单元。默认的app.module.ts
文件如下:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
@Module({
imports: [],
controllers: [AppController],
providers: [],
})
export class AppModule {}
在这个文件中,@Module
装饰器定义了一个模块。controllers
数组中包含了一个控制器AppController
,表示该模块中包含了这个控制器。
main.ts
main.ts
是应用程序的启动文件。默认的main.ts
文件如下:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
在这个文件中,NestFactory.create(AppModule)
创建了一个应用程序实例,并使用app.listen(3000)
启动了监听端口为3000的服务器。
要运行应用,可以使用以下命令:
npm run start
启动命令会启动一个开发服务器,默认监听端口3000。可以通过访问http://localhost:3000/app
来访问应用,返回的响应为Hello World!
。
控制器是处理HTTP请求和响应的组件。每个控制器都包含一个或多个处理HTTP请求的方法。控制器通过装饰器定义路由和请求类型。
创建控制器
要创建一个新的控制器,可以使用Nest CLI命令:
nest generate controller my-controller
上述命令会生成一个名为my-controller
的新控制器。生成的文件如下:
src/my-controller/my-controller.controller.ts
默认的my-controller.controller.ts
文件如下:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
@Controller('my-controller')
export class MyController {
@Get()
getHello(): string {
return 'Hello World!';
}
@Post()
create(@Body() data: any): any {
return data;
}
@Get(':id')
getOne(@Param('id') id: string): string {
return `This is the item with ID ${id}`;
}
}
在这个文件中,@Controller('my-controller')
定义了一个名为my-controller
的控制器。@Get()
装饰器定义了一个GET请求路由,返回一个字符串'Hello World!'
。@Post()
装饰器定义了一个POST请求路由,接收一个请求体data
并返回。@Get(':id')
装饰器定义了一个带参数的GET请求路由,接收一个路径参数id
并返回一个字符串。
使用控制器
要使用这个控制器,需要在模块中导入它。在app.module.ts
文件中,修改controllers
数组,添加新的控制器:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { MyController } from './my-controller/my-controller.controller';
@Module({
imports: [],
controllers: [AppController, MyController],
providers: [],
})
export class AppModule {}
现在,可以通过访问http://localhost:3000/my-controller
、http://localhost:3000/my-controller
和http://localhost:3000/my-controller/:id
来测试这些路由。
路由是定义HTTP请求的方法和参数的途径。Nest框架提供了多种装饰器来定义路由,常用的装饰器包括@Get
、@Post
、@Put
、@Delete
等。
基本路由定义
以下是一些基本的路由定义示例:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get()
getAll(): string {
return 'Get all users';
}
@Post()
create(@Body() user: any): any {
return user;
}
@Get(':id')
getOne(@Param('id') id: string): string {
return `This is the user with ID ${id}`;
}
@Put(':id')
update(@Param('id') id: string, @Body() user: any): string {
return `Update user with ID ${id}`;
}
@Delete(':id')
delete(@Param('id') id: string): string {
return `Delete user with ID ${id}`;
}
}
在这个示例中,@Controller('users')
定义了一个名为users
的控制器。@Get()
定义了一个GET所有用户的路由,@Post()
定义了一个POST创建用户的路由,@Get(':id')
定义了一个GET获取单个用户的路由,@Put(':id')
定义了一个PUT更新用户的路由,@Delete(':id')
定义了一个DELETE删除用户的路由。
请求处理函数是控制器中最重要的一部分,它们处理HTTP请求并返回响应。请求处理函数可以通过装饰器定义路由和请求类型。
请求处理函数示例
以下是一些请求处理函数的示例:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
@Controller('users')
export class UserController {
@Get()
getAll(): string {
return 'Get all users';
}
@Post()
create(@Body() user: any): any {
return user;
}
@Get(':id')
getOne(@Param('id') id: string): string {
return `This is the user with ID ${id}`;
}
@Put(':id')
update(@Param('id') id: string, @Body() user: any): string {
return `Update user with ID ${id}`;
}
@Delete(':id')
delete(@Param('id') id: string): string {
return `Delete user with ID ${id}`;
}
}
在这个示例中,getAll
方法处理GET请求,create
方法处理POST请求,getOne
方法处理带路径参数的GET请求,update
方法处理带路径参数的PUT请求,delete
方法处理带路径参数的DELETE请求。
服务是用于处理业务逻辑的组件。服务可以通过依赖注入的方式在控制器中使用。服务通常定义在专门的服务模块中。
创建服务
要创建一个新的服务,可以使用Nest CLI命令:
nest generate service my-service
上述命令会生成一个名为my-service
的新服务。生成的文件如下:
src/my-service/my-service.service.ts
默认的my-service.service.ts
文件如下:
import { Injectable } from '@nestjs/common';
@Injectable()
export class MyService {
getData(): string {
return 'Some data';
}
}
在这个文件中,@Injectable()
装饰器标记这个类为可注入的服务。getData()
方法是一个简单的示例方法,返回一些数据。
使用服务
要使用这个服务,需要在模块中导入它,并在控制器中注入它。在app.module.ts
文件中,修改providers
数组,添加新的服务:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { MyController } from './my-controller/my-controller.controller';
import { MyService } from './my-service/my-service.service';
@Module({
imports: [],
controllers: [AppController, MyController],
providers: [MyService],
})
export class AppModule {}
在控制器中注入服务,可以使用constructor
注入:
import { Controller, Get } from '@nestjs/common';
import { MyService } from '../my-service/my-service.service';
@Controller('my-controller')
export class MyController {
constructor(private readonly myService: MyService) {}
@Get()
getHello(): string {
return this.myService.getData();
}
}
现在,可以通过访问http://localhost:3000/my-controller
来测试控制器中注入的服务。
模块是Nest框架的组织单元。模块定义了应用程序的结构和依赖关系。每个模块可以包含一个或多个控制器、服务、管道、拦截器等。
模块示例
以下是一个简单的模块示例:
import { Module } from '@nestjs/common';
import { MyController } from './my-controller';
import { MyService } from './my-service';
@Module({
imports: [],
controllers: [MyController],
providers: [MyService],
})
export class MyModule {}
在这个示例中,@Module
装饰器定义了一个模块。controllers
数组中包含了一个控制器MyController
,providers
数组中包含了一个服务MyService
。
依赖注入是一种设计模式,它将对象的依赖关系从代码中解耦,使得代码更加解耦和可测试。在Nest框架中,依赖注入是通过装饰器和DI容器来实现的。
依赖注入示例
以下是一个依赖注入的示例:
import { Injectable } from '@nestjs/common';
@Injectable()
export class MyService {
getData(): string {
return 'Some data';
}
}
import { Controller, Get } from '@nestjs/common';
import { MyService } from '../my-service';
@Controller('my-controller')
export class MyController {
constructor(private readonly myService: MyService) {}
@Get()
getHello(): string {
return this.myService.getData();
}
}
在这个示例中,@Injectable()
装饰器标记MyService
类为可注入的服务。在MyController
中,通过constructor
注入了MyService
服务。这样,在控制器中可以直接使用注入的服务,而不需要关心服务的创建和初始化。
要集成数据库,需要先安装相应的数据库驱动和ORM工具。例如,要集成MySQL数据库,可以使用@nestjs/typeorm
和@nestjs/sequelize
等库。以下是安装@nestjs/typeorm
的命令:
npm install --save @nestjs/typeorm typeorm mysql2
安装完成后,可以在app.module.ts
文件中配置数据库连接:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MyController } from './my-controller';
import { MyService } from './my-service';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'mydatabase',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
controllers: [MyController],
providers: [MyService],
})
export class AppModule {}
在这个示例中,TypeOrmModule.forRoot()
方法配置了数据库连接的元数据。type
属性指定了数据库类型,host
、port
、username
、password
、database
属性指定了数据库的连接信息,entities
属性指定了实体文件的位置,synchronize
属性指定了是否自动同步实体。
ORM(对象关系映射)工具是用于映射数据库表和实体对象的工具。Nest框架提供了多种ORM工具,如TypeORM和Sequelize。以下是使用TypeORM的示例:
创建实体
要创建一个实体,可以使用Nest CLI命令:
nest generate entity my-entity
上述命令会生成一个名为my-entity
的新实体。生成的文件如下:
src/my-entity/my-entity.entity.ts
默认的my-entity.entity.ts
文件如下:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class MyEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
在这个文件中,@Entity()
装饰器定义了一个实体类。@PrimaryGeneratedColumn()
装饰器定义了一个自增的主键列id
,@Column()
装饰器定义了一个普通列name
。
使用实体
要在服务中使用实体,需要在模块中导入实体,并在服务中使用实体的TypeORM方法。在app.module.ts
文件中,添加TypeOrmModule.forFeature([MyEntity])
:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MyController } from './my-controller';
import { MyService } from './my-service';
import { MyEntity } from './my-entity';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'mydatabase',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
TypeOrmModule.forFeature([MyEntity]),
],
controllers: [MyController],
providers: [MyService],
})
export class AppModule {}
在服务中使用实体,可以使用TypeORM的getRepository
方法:
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { MyEntity } from '../my-entity';
@Injectable()
export class MyService {
constructor(private readonly myEntityRepository: Repository<MyEntity>) {}
async create(name: string): Promise<MyEntity> {
const myEntity = this.myEntityRepository.create({ name });
return this.myEntityRepository.save(myEntity);
}
async findAll(): Promise<MyEntity[]> {
return this.myEntityRepository.find();
}
async findOne(id: number): Promise<MyEntity> {
return this.myEntityRepository.findOne(id);
}
async update(id: number, name: string): Promise<MyEntity> {
const myEntity = this.myEntityRepository.create({ id, name });
return this.myEntityRepository.save(myEntity);
}
async remove(id: number): Promise<void> {
await this.myEntityRepository.delete(id);
}
}
在这个示例中,@Injectable()
装饰器标记MyService
类为可注入的服务。在构造函数中注入了Repository<MyEntity>
,表示注入了MyEntity
实体的TypeORM仓库。在服务中使用仓库的create
、save
、find
、findOne
、save
、delete
等方法来操作数据库。
CRUD(创建、读取、更新、删除)操作是数据库中最常见的操作。以下是使用TypeORM实现CRUD操作的示例:
创建实体
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class MyEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
创建服务
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { MyEntity } from '../my-entity';
@Injectable()
export class MyService {
constructor(private readonly myEntityRepository: Repository<MyEntity>) {}
async create(name: string): Promise<MyEntity> {
const myEntity = this.myEntityRepository.create({ name });
return this.myEntityRepository.save(myEntity);
}
async findAll(): Promise<MyEntity[]> {
return this.myEntityRepository.find();
}
async findOne(id: number): Promise<MyEntity> {
return this.myEntityRepository.findOne(id);
}
async update(id: number, name: string): Promise<MyEntity> {
const myEntity = this.myEntityRepository.create({ id, name });
return this.myEntityRepository.save(myEntity);
}
async remove(id: number): Promise<void> {
await this.myEntityRepository.delete(id);
}
}
在这个示例中,@Injectable()
装饰器标记MyService
类为可注入的服务。在构造函数中注入了Repository<MyEntity>
,表示注入了MyEntity
实体的TypeORM仓库。在服务中使用仓库的create
、save
、find
、findOne
、save
、delete
等方法来实现创建、读取、更新、删除操作。
使用控制器
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { MyService } from '../my-service';
@Controller('my-entity')
export class MyController {
constructor(private readonly myService: MyService) {}
@Post()
create(@Body() myEntity: MyEntity): Promise<MyEntity> {
return this.myService.create(myEntity.name);
}
@Get()
findAll(): Promise<MyEntity[]> {
return this.myService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string): Promise<MyEntity> {
return this.myService.findOne(+id);
}
@Put(':id')
update(@Param('id') id: string, @Body() myEntity: MyEntity): Promise<MyEntity> {
return this.myService.update(+id, myEntity.name);
}
@Delete(':id')
remove(@Param('id') id: string): Promise<void> {
return this.myService.remove(+id);
}
}
在这个示例中,@Post()
定义了一个创建实体的路由,@Get()
定义了一个读取所有实体的路由,@Get(':id')
定义了一个读取单个实体的路由,@Put(':id')
定义了一个更新实体的路由,@Delete(':id')
定义了一个删除实体的路由。
在Nest框架中,可以通过自定义错误类和全局异常过滤器来实现错误处理。全局异常过滤器可以捕获所有未捕获的异常,并返回统一的错误响应。
自定义错误类
自定义错误类可以定义特定的错误类型和错误信息。以下是一个自定义错误类的示例:
import { HttpException, HttpStatus } from '@nestjs/common';
export class CustomException extends HttpException {
constructor(message: string, status: HttpStatus) {
super(message, status);
}
}
在这个示例中,CustomException
类继承了HttpException
类,并提供了构造函数来初始化错误信息和状态码。
全局异常过滤器
全局异常过滤器可以捕获所有未捕获的异常,并返回统一的错误响应。以下是一个全局异常过滤器的示例:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
@Catch()
export class AllExceptionFilter implements ExceptionFilter {
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
response.status = status;
response.json({
message: exception.message,
code: status,
});
}
}
在这个示例中,AllExceptionFilter
类实现了ExceptionFilter
接口,并提供了catch
方法来捕获异常。catch
方法获取请求上下文,返回响应,设置响应状态码,并返回统一的错误响应。
使用全局异常过滤器
要在模块中使用全局异常过滤器,需要在模块的@Module
装饰器中添加@Global()
装饰器,并在providers
数组中添加全局异常过滤器。
import { Module, Global } from '@nestjs/common';
import { AllExceptionFilter } from './all-exception.filter';
@Global()
@Module({
providers: [AllExceptionFilter],
})
export class CommonModule {}
日志记录的设置和使用
日志记录对于调试和维护应用程序非常重要。在Nest框架中,可以通过配置日志中间件和使用日志库来实现日志记录。
配置日志中间件
Nest框架提供了多种日志中间件,如Pino和Winston。以下是一个配置Pino日志中间件的示例:
npm install --save @nestjs/common @nestjs/core pino
在main.ts
文件中,添加日志中间件:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PinoLoggerService } from './pino-logger.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useLogger(new PinoLoggerService());
await app.listen(3000);
}
bootstrap();
在这个示例中,app.useLogger(new PinoLoggerService())
添加了一个日志中间件,使用了自定义的PinoLoggerService
服务。
自定义日志服务
自定义日志服务可以使用日志库来记录日志。以下是一个自定义日志服务的示例:
import { Injectable } from '@nestjs/common';
import pino from 'pino';
@Injectable()
export class PinoLoggerService {
private logger = pino({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
prettyPrint: process.env.NODE_ENV !== 'production',
});
log(message: string) {
this.logger.info(message);
}
error(message: string, error: Error) {
this.logger.error(message, error);
}
warn(message: string) {
this.logger.warn(message);
}
debug(message: string) {
this.logger.debug(message);
}
trace(message: string) {
this.logger.trace(message);
}
}
在这个示例中,PinoLoggerService
类使用了pino
库来记录日志。通过level
属性和prettyPrint
属性配置了日志级别和格式。
使用日志服务
要在服务和控制器中使用日志服务,需要在构造函数中注入日志服务,并使用log
、error
、warn
、debug
、trace
等方法来记录日志。
import { Injectable } from '@nestjs/common';
import { PinoLoggerService } from '../pino-logger.service';
@Injectable()
export class MyService {
constructor(private readonly logger: PinoLoggerService) {}
async create(name: string): Promise<MyEntity> {
this.logger.log(`Creating entity with name ${name}`);
const myEntity = this.myEntityRepository.create({ name });
return this.myEntityRepository.save(myEntity);
}
async findAll(): Promise<MyEntity[]> {
this.logger.log('Finding all entities');
return this.myEntityRepository.find();
}
}
在这个示例中,在服务的构造函数中注入了日志服务,并在方法中使用了log
方法来记录日志。
调试是开发过程中非常重要的一部分。以下是一些调试技巧和最佳实践:
调试技巧
- 日志记录:通过记录日志来跟踪应用程序的状态和行为。
- 断点调试:在IDE中设置断点来暂停执行和查看变量值。
- 单元测试:通过编写单元测试来验证代码的正确性。
- 代码审查:通过代码审查来发现潜在的问题和改进点。
调试最佳实践
- 模块化:将代码组织成模块化结构,便于调试和维护。
- 依赖注入:使用依赖注入来解耦代码,便于调试和测试。
- 错误处理:通过自定义错误类和全局异常过滤器来捕获和处理错误。
- 日志记录:通过配置日志中间件和使用日志库来记录日志。
- 单元测试:通过编写单元测试来验证代码的正确性。
- 持续集成:通过持续集成来自动构建和测试代码。
总结
通过以上内容的学习,可以掌握Nest框架的基本概念、安装和配置、创建第一个应用、控制器与路由、服务与模块、数据库集成、错误处理与日志记录等基础知识。掌握这些知识后,可以进一步学习和实践更复杂的功能和特性,如GraphQL、WebSocket、认证和授权等。
共同学习,写下你的评论
评论加载中...
作者其他优质文章