NestJS 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架,它采用了TypeScript,提供了类型检查和静态类型支持。NestJS 设计理念受到了Angular和Node.js等成熟框架的影响,强调模块化、可维护性和可测试性。本文将详细介绍NestJS的核心特性、环境搭建、基础概念以及实战演练等内容,帮助读者全面掌握Nest学习。
Nest 是一个用于构建高效、可扩展的 Node.js 服务器端应用程序的框架。NestJS 是用 TypeScript 编写的,这意味着它提供了类型检查和强大的静态类型支持,这有助于减少运行时错误。NestJS 设计理念受到Angular和Node.js等成熟框架的影响,强调模块化、可维护性和可测试性。
Nest主要特点是采用装饰器模式,提供了丰富的装饰器,如@Controller
、@InjectRepository
等,使得代码结构更加清晰和易于理解。它还支持多种数据库集成,如MongoDB、MySQL、PostgreSQL等,并且可以轻松地与多种第三方服务集成,如OAuth、TypeORM等。
@Controller
、@Injectable
、@Post
等装饰器。NestJS 可以用于构建多种类型的服务器端应用,包括但不限于:
要开始使用 NestJS,首先需要确保安装了 Node.js。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它使用一个事件驱动、非阻塞 I/O 的模型,使其轻量且高效。以下是如何安装 Node.js 的步骤:
为了验证安装成功,可以在命令行中执行以下命令:
node -v npm -v
输出应显示安装的 Node.js 和 npm 版本号。
安装 Node.js 后,还需要安装 Nest CLI,这是一个命令行工具,用于生成和管理 NestJS 项目。以下是安装 Nest CLI 的步骤:
npm install -g @nestjs/cli
安装完成后,可以使用以下命令来创建一个新的 NestJS 项目:
nest new my-nest-app
这将生成一个新的目录结构,其中包含了基本的 NestJS 应用程序代码。
生成好项目后,进入项目的根目录并启动应用:
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!” 页面。
在 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
装饰器用于定义模块的元数据,controllers
和 providers
字段分别包含模块中的控制器和服务。
服务(Service)在 NestJS 中扮演着重要的角色,它们负责处理业务逻辑,通常与数据库交互。服务类可以通过依赖注入的方式注入到控制器或其他服务中。以下是一个简单的服务示例:
import { Injectable } from '@nestjs/common'; @Injectable() export class UserService { getUsers(): string[] { return ['Alice', 'Bob', 'Charlie']; } }
使用 @Injectable
装饰器将类标记为可以被依赖注入的类。这个服务类定义了一个 getUsers
方法,用于返回一组用户。
控制器(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 请求处理器,并且在处理器方法中调用了 UserService
的 getUsers
方法来获取一组用户。
提供者(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
实例。
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 令牌进行验证。
要创建一个 RESTful API,可以使用 NestJS 提供的控制器和装饰器来定义路由和处理请求。以下是如何创建一个简单的 RESTful API 的步骤:
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); } }
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); } }
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
import { IsString } from 'class-validator'; export class CreateUserDto { @IsString() name: string; @IsString() email: string; } export class UpdateUserDto { @IsString() name: string; @IsString() email: string; }
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 {}
NestJS 中间件允许你在请求处理过程中插入自定义逻辑。例如,可以使用中间件来验证请求头中的 API 密钥或处理跨域资源共享(CORS)。以下是如何使用中间件的步骤:
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(); } }
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
注册中间件,使其应用于所有路由。
TypeORM 是一个强大的、基于装饰器的 ORM(对象关系映射)库,用于 Node.js 和 TypeScript。以下是如何使用 TypeORM 集成数据库的步骤:
npm install typeorm npm install --save @nestjs/typeorm @nestjs/common @nestjs/core @nestjs/platform-express
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 {}
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; }
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); } }
虽然 NestJS 通常用于构建 RESTful API,但它也可以与模板引擎(如 Handlebars)结合使用来生成动态 HTML 页面。以下是如何使用 Handlebars 的步骤:
npm install handlebars @nestjs/core @nestjs/common @nestjs/serve-static @nestjs/platform-express
import { Module } from '@nestjs/common'; import { HandlebarsAdapter, HandlebarsEngine } from '@nestjs/platform-handlebars'; @Module({ imports: [ HandlebarsEngine.register({ adapter: new HandlebarsAdapter(), }), ], }) export class AppModule {}
import { Controller, Get, Render } from '@nestjs/common'; @Controller() export class PageController { @Get('home') @Render('home') getHome() { return { title: 'My NestJS App' }; } }
在项目的 views
目录下创建一个 home.handlebars
文件:
<!DOCTYPE html> <html> <head> <title>{{title}}</title> </head> <body> <h1>Welcome to My NestJS App</h1> </body> </html>
npm run start
访问 http://localhost:3000/home
,将看到一个使用 Handlebars 模板引擎生成的动态 HTML 页面。
错误:Cannot find module '@nestjs/common'
如果遇到这个错误,表示你可能没有正确安装 @nestjs/common
包。可以通过以下命令重新安装:
npm install @nestjs/common
错误:Module '...' has no exported member '...'
这个错误通常出现在导入 @nestjs/common
模块时。确保你引用了正确的模块路径,例如:
import { Module } from '@nestjs/common';
错误:Cannot read property '...' of undefined
如果你的代码中使用了未定义的变量或方法,请检查代码并确保所有引用的对象和方法已正确初始化。例如:
export class SomeService { private data: any = {}; getData() { return this.data; } }
错误:Cannot find name '...'
这个错误通常出现在类型定义丢失的情况下。可以通过安装相应包的类型定义来解决。例如,使用 TypeScript 时,确保安装了 @types/node
:
npm install @types/node
环境配置
在 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
。
自定义配置文件
可以创建自定义配置文件,例如 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 {}
错误处理
在 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 {}
使用缓存
在高并发场景下,可以通过缓存来减少数据库查询次数,提高应用响应速度。可以使用 @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 {}
使用代理服务器
使用反向代理服务器(如 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; } }
优化数据库访问
在数据库访问方面,可以通过以下方式优化性能:
例如,使用 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' }, }); } }
NestJS 官方文档提供了详尽的教程和指南,涵盖了从安装、基本概念到高级特性的各个方面。官方文档是学习 NestJS 的最佳资源,以下是文档的主要部分:
NestJS 社区非常活跃,提供了丰富的资源和帮助:
以下是一些推荐的开发工具,可以帮助你在 NestJS 项目中提高开发效率:
通过掌握 NestJS 的核心概念和最佳实践,开发者可以构建高效、可维护的 Node.js 服务器端应用程序。NestJS 的模块化架构和强大的依赖注入功能,使其成为构建复杂应用程序的理想选择。