Java教程

Nest学习:初学者指南

本文主要是介绍Nest学习:初学者指南,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概述

NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架,它采用了TypeScript,提供了类型检查和静态类型支持。NestJS 设计理念受到了Angular和Node.js等成熟框架的影响,强调模块化、可维护性和可测试性。本文将详细介绍NestJS的核心特性、环境搭建、基础概念以及实战演练等内容,帮助读者全面掌握Nest学习。

1. Nest简介

1.1 什么是Nest

Nest 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。NestJS 是用 TypeScript 编写的,这意味着它提供了类型检查和强大的静态类型支持,这有助于减少运行时错误。NestJS 设计理念受到Angular和Node.js等成熟框架的影响,强调模块化、可维护性和可测试性。

Nest主要特点是采用装饰器模式,提供了丰富的装饰器,如@Controller@InjectRepository等,使得代码结构更加清晰和易于理解。它还支持多种数据库集成,如MongoDB、MySQL、PostgreSQL等,并且可以轻松地与多种第三方服务集成,如OAuth、TypeORM等。

1.2 Nest的核心特性

  1. 装饰器模式:Nest 使用装饰器来定义组件的行为和属性。例如,@Controller@Injectable@Post等装饰器。
  2. 可插拔中间件:Nest 提供了中间件机制,允许开发者轻松地在请求处理流程中插入自定义逻辑。中间件可以用于日志记录、身份验证、请求解析等。
  3. 依赖注入:依赖注入(DI)是 Nest 的核心特性之一。它允许模块和组件之间通过接口和抽象来解耦。使用装饰器,Nest 能够自动管理依赖关系并注入所需的服务。
  4. 模块化架构:Nest 采用模块化架构,允许开发者将应用程序拆分为多个小的模块,每个模块负责特定的功能。这有助于提高代码的可维护性和可重用性。

1.3 Nest的应用场景

NestJS 可以用于构建多种类型的服务器端应用,包括但不限于:

  1. Web 应用:构建 RESTful API、单页应用(SPA)后端。
  2. 微服务:使用 NestJS 构建独立且可扩展的微服务。
  3. GraphQL API:NestJS 提供了对 GraphQL 的支持,可以快速地构建 GraphQL API。
  4. 命令行工具:使用 NestJS 构建命令行工具或脚本。

2. 环境搭建

2.1 安装Node.js

要开始使用 NestJS,首先需要确保安装了 Node.js。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它使用一个事件驱动、非阻塞 I/O 的模型,使其轻量且高效。以下是如何安装 Node.js 的步骤:

  1. 访问 Node.js 官方网站 (https://nodejs.org/) 并下载最新版本的 Node.js。
  2. 按照下载页面上的指引完成安装。

为了验证安装成功,可以在命令行中执行以下命令:

node -v
npm -v

输出应显示安装的 Node.js 和 npm 版本号。

2.2 创建Nest项目

安装 Node.js 后,还需要安装 Nest CLI,这是一个命令行工具,用于生成和管理 NestJS 项目。以下是安装 Nest CLI 的步骤:

  1. 打开命令行工具。
  2. 使用 npm 安装 Nest CLI:
npm install -g @nestjs/cli

安装完成后,可以使用以下命令来创建一个新的 NestJS 项目:

nest new my-nest-app

这将生成一个新的目录结构,其中包含了基本的 NestJS 应用程序代码。

2.3 运行第一个Nest应用

生成好项目后,进入项目的根目录并启动应用:

cd my-nest-app
npm run start

启动应用后,会在控制台看到类似以下输出:

[Nest] 14432   -   [Main]   Initializing module.
[Nest] 14432   -   [Main]   Module initialized!
[Nest] 14432   -   [Main]   App is running at http://localhost:3000
[Nest] 14432   -   [Main]   📣  Access your API at http://localhost:3000
[Nest] 14432   -   [Main]   🍾  Development mode: true

现在打开浏览器访问 http://localhost:3000,将看到一个简单的 “Hello, World!” 页面。

3. 基础概念

3.1 模块(Modules)

在 NestJS 中,模块(Module)是应用程序的基础构建块。每个模块都包含一组相关的服务和控制器,可以独立使用或依赖其他模块。模块的目的是将应用程序分割成多个部分,每个部分负责一个特定的功能。

模块定义了应用程序中的边界,包括组件的生命周期,包括控制器、服务和提供者。模块之间可以相互依赖,实现模块化开发。以下是模块的基本结构:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

在这个例子中,UsersModule 模块定义了一个控制器 UsersController 和一个服务 UsersService。这里的 @Module 装饰器用于定义模块的元数据,controllersproviders 字段分别包含模块中的控制器和服务。

3.2 服务(Services)

服务(Service)在 NestJS 中扮演着重要的角色,它们负责处理业务逻辑,通常与数据库交互。服务类可以通过依赖注入的方式注入到控制器或其他服务中。以下是一个简单的服务示例:

import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  getUsers(): string[] {
    return ['Alice', 'Bob', 'Charlie'];
  }
}

使用 @Injectable 装饰器将类标记为可以被依赖注入的类。这个服务类定义了一个 getUsers 方法,用于返回一组用户。

3.3 控制器(Controllers)

控制器(Controller)是处理 HTTP 请求的地方。每个控制器处理一组相关的路由,并且可以调用服务来处理具体的业务逻辑。控制器通过装饰器来定义路由和 HTTP 请求方法。以下是一个简单的控制器示例:

import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  getUsers(): string[] {
    return this.userService.getUsers();
  }
}

在这个例子中,UserController 控制器使用 @Controller 装饰器定义为处理 /users 路径下的请求。@Get 装饰器用于定义一个 HTTP GET 请求处理器,并且在处理器方法中调用了 UserServicegetUsers 方法来获取一组用户。

3.4 提供者(Providers)

提供者(Provider)在 NestJS 中是一个非常重要的概念,它允许组件之间解耦,并且可以共享服务实例。提供者可以是服务、拦截器、管道、装饰器或者其他任何可以注入到依赖注入容器中的对象。提供者的定义通常在模块的 providers 字段中:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

在这个例子中,UsersService 是一个提供者。当在控制器或其他地方注入 UsersService 时,NestJS 会自动提供一个 UsersService 实例。

3.5 装饰器(Decorators)

NestJS 中的装饰器(Decorators)可以用于定义组件的行为和属性,如控制器、服务和中间件等。装饰器是 NestJS 设计的核心部分,通过装饰器可以轻松地定义和扩展组件的功能。以下是一些常用的装饰器:

  • @Controller:定义一个控制器,处理一组特定的路由。
  • @Injectable:标记一个类可以被依赖注入。
  • @Get@Post@Put@Delete:定义 HTTP 请求处理器。
  • @InjectRepository:注入一个数据访问对象(DAO)。
  • @UseGuards:使用自定义的守卫来保护特定的路由。
  • @Inject:用于手动注入依赖。
  • @Service:标记一个类为服务类。
import { Controller, Get, Post, Put, Delete, UseGuards } from '@nestjs/common';
import { UserService } from './user.service';
import { AuthGuard } from '@nestjs/passport';

@Controller('users')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get()
  getUsers(): string[] {
    return this.userService.getUsers();
  }

  @Post()
  @UseGuards(AuthGuard('jwt'))
  addUser(user: string): string {
    return this.userService.addUser(user);
  }

  @Put()
  updateUser(user: string): string {
    return this.userService.updateUser(user);
  }

  @Delete()
  deleteUser(user: string): string {
    return this.userService.deleteUser(user);
  }
}

在这个例子中,使用了 @Controller 装饰器定义了一个控制器,处理 /users 路径下的请求。使用 @Get@Post@Put@Delete 定义了相应的 HTTP 请求处理器,并且在 @Post 请求处理器中使用了 @UseGuards 来保护该路由,需要 JWT 令牌进行验证。

4. 实战演练

4.1 创建RESTful API

要创建一个 RESTful API,可以使用 NestJS 提供的控制器和装饰器来定义路由和处理请求。以下是如何创建一个简单的 RESTful API 的步骤:

  1. 创建控制器和服务
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from './user.entity';
import { CreateUserDto } from './create-user.dto';
import { UpdateUserDto } from './update-user.dto';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll(): User[] {
    return this.usersService.findAll();
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto): User {
    return this.usersService.create(createUserDto);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto): User {
    return this.usersService.update(id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string): void {
    this.usersService.remove(id);
  }
}
  1. 定义服务
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './create-user.dto';
import { UpdateUserDto } from './update-user.dto';

@Injectable()
export class UsersService {
  constructor(@InjectRepository(User) private usersRepository: Repository<User>) {}

  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return this.usersRepository.save(user);
  }

  async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.usersRepository.findOne(id);
    if (!user) {
      throw new Error('User not found');
    }
    Object.assign(user, updateUserDto);
    return this.usersRepository.save(user);
  }

  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}
  1. 定义数据模型
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}
  1. 定义 DTO(数据传输对象)
import { IsString } from 'class-validator';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsString()
  email: string;
}

export class UpdateUserDto {
  @IsString()
  name: string;

  @IsString()
  email: string;
}
  1. 注册模块
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

4.2 使用中间件

NestJS 中间件允许你在请求处理过程中插入自定义逻辑。例如,可以使用中间件来验证请求头中的 API 密钥或处理跨域资源共享(CORS)。以下是如何使用中间件的步骤:

  1. 定义中间件
import { Injectable, NestMiddleware } from '@nestjs/common';

@Injectable()
export class CorsMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    next();
  }
}
  1. 注册中间件
import { Module } from '@nestjs/common';
import { CorsMiddleware } from './cors.middleware';

@Module({
  providers: [CorsMiddleware],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CorsMiddleware)
      .forRoutes('*');
  }
}

在这个例子中,CorsMiddleware 是一个简单的中间件,用于处理跨域资源共享(CORS)。它使用 @Injectable 装饰器标记为可以被依赖注入的类,并且实现 NestMiddleware 接口。在 AppModule 中通过 MiddlewareConsumer 注册中间件,使其应用于所有路由。

4.3 数据库集成(例如:TypeORM)

TypeORM 是一个强大的、基于装饰器的 ORM(对象关系映射)库,用于 Node.js 和 TypeScript。以下是如何使用 TypeORM 集成数据库的步骤:

  1. 安装依赖
npm install typeorm
npm install --save @nestjs/typeorm @nestjs/common @nestjs/core @nestjs/platform-express
  1. 配置数据源
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'mydb',
      entities: [User],
      synchronize: true,
    }),
  ],
  imports: [TypeOrmModule.forFeature([User])],
})
export class UsersModule {}
  1. 定义数据模型
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}
  1. 定义服务
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './create-user.dto';
import { UpdateUserDto } from './update-user.dto';

@Injectable()
export class UsersService {
  constructor(@InjectRepository(User) private usersRepository: Repository<User>) {}

  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return this.usersRepository.save(user);
  }

  async update(id: string, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.usersRepository.findOne(id);
    if (!user) {
      throw new Error('User not found');
    }
    Object.assign(user, updateUserDto);
    return this.usersRepository.save(user);
  }

  async remove(id: string): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

4.4 模板引擎(例如:Handlebars)

虽然 NestJS 通常用于构建 RESTful API,但它也可以与模板引擎(如 Handlebars)结合使用来生成动态 HTML 页面。以下是如何使用 Handlebars 的步骤:

  1. 安装依赖
npm install handlebars @nestjs/core @nestjs/common @nestjs/serve-static @nestjs/platform-express
  1. 配置模板引擎
import { Module } from '@nestjs/common';
import { HandlebarsAdapter, HandlebarsEngine } from '@nestjs/platform-handlebars';

@Module({
  imports: [
    HandlebarsEngine.register({
      adapter: new HandlebarsAdapter(),
    }),
  ],
})
export class AppModule {}
  1. 定义控制器
import { Controller, Get, Render } from '@nestjs/common';

@Controller()
export class PageController {
  @Get('home')
  @Render('home')
  getHome() {
    return { title: 'My NestJS App' };
  }
}
  1. 创建视图文件

在项目的 views 目录下创建一个 home.handlebars 文件:

<!DOCTYPE html>
<html>
<head>
    <title>{{title}}</title>
</head>
<body>
    <h1>Welcome to My NestJS App</h1>
</body>
</html>
  1. 启动应用并访问页面
npm run start

访问 http://localhost:3000/home,将看到一个使用 Handlebars 模板引擎生成的动态 HTML 页面。

5. 常见问题解答

5.1 常见错误及解决方案

  1. 错误:Cannot find module '@nestjs/common'

    如果遇到这个错误,表示你可能没有正确安装 @nestjs/common 包。可以通过以下命令重新安装:

    npm install @nestjs/common
  2. 错误:Module '...' has no exported member '...'

    这个错误通常出现在导入 @nestjs/common 模块时。确保你引用了正确的模块路径,例如:

    import { Module } from '@nestjs/common';
  3. 错误:Cannot read property '...' of undefined

    如果你的代码中使用了未定义的变量或方法,请检查代码并确保所有引用的对象和方法已正确初始化。例如:

    export class SomeService {
     private data: any = {};
    
     getData() {
       return this.data;
     }
    }
  4. 错误:Cannot find name '...'

    这个错误通常出现在类型定义丢失的情况下。可以通过安装相应包的类型定义来解决。例如,使用 TypeScript 时,确保安装了 @types/node

    npm install @types/node

5.2 配置问题详解

  1. 环境配置

    在 NestJS 中,可以通过环境变量来配置应用程序。以下是如何在 app.module.ts 中使用环境变量:

    import { Module } from '@nestjs/common';
    import { ConfigService } from '@nestjs/config';
    import * as process from 'process';
    
    @Module({
     providers: [
       ConfigService,
       {
         provide: 'DATABASE_URL',
         useFactory: (configService: ConfigService) => configService.get('DATABASE_URL'),
         inject: [ConfigService],
       },
     ],
    })
    export class AppModule {}

    这里首先导入 ConfigService,然后在 useFactory 函数中使用 ConfigService 获取环境变量 DATABASE_URL

  2. 自定义配置文件

    可以创建自定义配置文件,例如 app.config.ts

    export interface AppConfig {
     port: number;
     databaseUrl: string;
    }

    然后在 AppModule 中使用这个配置文件:

    import { Module } from '@nestjs/common';
    import { ConfigModule, ConfigService } from '@nestjs/config';
    import { appConfig } from './app.config';
    
    @Module({
     imports: [
       ConfigModule.forRoot({
         envFilePath: '.env',
         load: [appConfig],
       }),
     ],
     providers: [ConfigService],
    })
    export class AppModule {}
  3. 错误处理

    在 NestJS 中处理错误时,可以使用全局异常过滤器 GlobalExceptionFilter

    import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
    import { Request, Response } from 'express';
    
    @Catch()
    export class GlobalExceptionFilter implements ExceptionFilter {
     catch(exception: any, host: ArgumentsHost) {
       const ctx = host.switchToHttp();
       const response = ctx.getResponse<Response>();
       const request = ctx.getRequest<Request>();
       const status = exception.status || 500;
    
       response.status = status;
       response.json({
         statusCode: status,
         timestamp: new Date().toISOString(),
         path: request.url,
       });
     }
    }

    然后在 AppModule 中注册过滤器:

    import { Module } from '@nestjs/common';
    import { GlobalExceptionFilter } from './global-exception.filter';
    
    @Module({
     providers: [GlobalExceptionFilter],
    })
    export class AppModule {}

5.3 性能优化入门

  1. 使用缓存

    在高并发场景下,可以通过缓存来减少数据库查询次数,提高应用响应速度。可以使用 @nestjs/cache-manager 模块来实现缓存功能:

    import { Module } from '@nestjs/common';
    import { CacheModule } from '@nestjs/cache-manager';
    import * as cache from 'memory-cache';
    
    @Module({
     imports: [
       CacheModule.register({
         ttl: 10,
         store: cache,
       }),
     ],
    })
    export class AppModule {}
  2. 使用代理服务器

    使用反向代理服务器(如 Nginx)可以缓解直接访问应用服务器的情况,从而提高应用性能。以下是一个简单的 Nginx 配置文件示例:

    server {
     listen 80;
     server_name example.com;
    
     location / {
       proxy_pass http://localhost:3000;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection 'upgrade';
       proxy_set_header Host $host;
       proxy_cache_bypass $http_upgrade;
     }
    }
  3. 优化数据库访问

    在数据库访问方面,可以通过以下方式优化性能:

    • 使用分页(Paging)和分块(Chunking)技术,限制每次查询的数据量。
    • 使用索引(Index)加速查询。
    • 优化查询语句,避免复杂的子查询或联合查询。

    例如,使用 typeorm 进行分页查询:

    import { Repository } from 'typeorm';
    
    export class UsersService {
     constructor(private readonly userRepository: Repository<User>) {}
    
     async findAll(page: number, limit: number): Promise<User[]> {
       return await this.userRepository.find({
         take: limit,
         skip: (page - 1) * limit,
         order: { id: 'ASC' },
       });
     }
    }

6. 资源推荐

6.1 官方文档

NestJS 官方文档提供了详尽的教程和指南,涵盖了从安装、基本概念到高级特性的各个方面。官方文档是学习 NestJS 的最佳资源,以下是文档的主要部分:

  • 安装:https://docs.nestjs.com/first-steps/first-steps
  • 基础概念:https://docs.nestjs.com/techniques/modules
  • 高级主题:https://docs.nestjs.com/techniques/database
  • 中间件和过滤器:https://docs.nestjs.com/interceptors
  • 测试:https://docs.nestjs.com/techniques/testing

6.2 社区资源

NestJS 社区非常活跃,提供了丰富的资源和帮助:

  • GitHub 仓库:https://github.com/nestjs/nest
  • Stack Overflow:https://stackoverflow.com/questions/tagged/nestjs
  • Discord 社区:https://discord.gg/G7Qnnnq
  • 官方论坛:https://forum.nestjs.com/

6.3 开发工具推荐

以下是一些推荐的开发工具,可以帮助你在 NestJS 项目中提高开发效率:

  • VSCode:https://code.visualstudio.com/
  • ESLint:https://eslint.org/
  • Prettier:https://prettier.io/
  • TypeORM CLI:https://typeorm.io/cli
  • Nest CLI:https://docs.nestjs.com/cli/overview

通过掌握 NestJS 的核心概念和最佳实践,开发者可以构建高效、可维护的 Node.js 服务器端应用程序。NestJS 的模块化架构和强大的依赖注入功能,使其成为构建复杂应用程序的理想选择。

这篇关于Nest学习:初学者指南的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!