为了账号安全,请及时绑定邮箱和手机立即绑定

Nest后端开发入门指南

标签:
Java
概述

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框架的优势和特点

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:应用程序的启动文件。
  • 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-controllerhttp://localhost:3000/my-controllerhttp://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数组中包含了一个控制器MyControllerproviders数组中包含了一个服务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属性指定了数据库类型,hostportusernamepassworddatabase属性指定了数据库的连接信息,entities属性指定了实体文件的位置,synchronize属性指定了是否自动同步实体。

ORM工具的使用

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仓库。在服务中使用仓库的createsavefindfindOnesavedelete等方法来操作数据库。

CRUD操作实现

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仓库。在服务中使用仓库的createsavefindfindOnesavedelete等方法来实现创建、读取、更新、删除操作。

使用控制器

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属性配置了日志级别和格式。

使用日志服务

要在服务和控制器中使用日志服务,需要在构造函数中注入日志服务,并使用logerrorwarndebugtrace等方法来记录日志。

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方法来记录日志。

调试技巧与最佳实践

调试是开发过程中非常重要的一部分。以下是一些调试技巧和最佳实践:

调试技巧

  1. 日志记录:通过记录日志来跟踪应用程序的状态和行为。
  2. 断点调试:在IDE中设置断点来暂停执行和查看变量值。
  3. 单元测试:通过编写单元测试来验证代码的正确性。
  4. 代码审查:通过代码审查来发现潜在的问题和改进点。

调试最佳实践

  1. 模块化:将代码组织成模块化结构,便于调试和维护。
  2. 依赖注入:使用依赖注入来解耦代码,便于调试和测试。
  3. 错误处理:通过自定义错误类和全局异常过滤器来捕获和处理错误。
  4. 日志记录:通过配置日志中间件和使用日志库来记录日志。
  5. 单元测试:通过编写单元测试来验证代码的正确性。
  6. 持续集成:通过持续集成来自动构建和测试代码。

总结

通过以上内容的学习,可以掌握Nest框架的基本概念、安装和配置、创建第一个应用、控制器与路由、服务与模块、数据库集成、错误处理与日志记录等基础知识。掌握这些知识后,可以进一步学习和实践更复杂的功能和特性,如GraphQL、WebSocket、认证和授权等。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消