Nest是一个用于构建高效、可扩展的服务器端JavaScript应用程序的框架,它基于现代JavaScript框架Tsyrph,核心特性包括模块化、依赖注入、装饰器等。Nest后端开发提供了强大的工具和设计模式,帮助开发者构建高效且高度可测试的应用程序。通过集成数据库、错误处理和日志记录等功能,Nest框架支持更复杂的功能和特性。
Nest是一个用于构建高效、可扩展的服务器端JavaScript应用程序的框架。Nest框架基于Angular开发团队设计的现代JavaScript框架Tsyrph,它将许多先进的设计原则注入到Node.js中。Nest框架的核心特性包括模块化、依赖注入、装饰器、可插拔适配器等,它允许开发者使用TypeScript或JavaScript开发高效且高度可测试的应用程序。
Nest框架的主要特点有:
Nest框架的优势在于它可以为开发者提供强大的工具和设计模式,帮助他们构建高效、可维护和可扩展的Web应用程序。以下是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 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项目的目录结构如下:
控制器是处理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!'
。
模块是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
文件如下:
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
方法来记录日志。
调试是开发过程中非常重要的一部分。以下是一些调试技巧和最佳实践:
通过以上内容的学习,可以掌握Nest框架的基本概念、安装和配置、创建第一个应用、控制器与路由、服务与模块、数据库集成、错误处理与日志记录等基础知识。掌握这些知识后,可以进一步学习和实践更复杂的功能和特性,如GraphQL、WebSocket、认证和授权等。