NestJS 简介

NestJS 是基于 TypeScript 的渐进式 Node.js 框架,用于构建高效、可靠、可扩展的服务器端应用。它融合了面向对象编程、函数式编程和响应式编程的最佳实践,为开发者提供了一种结构化的方式来构建复杂的后端应用。

核心特点

  • 模块化架构:NestJS 采用模块化设计,使代码组织更加清晰,便于维护和扩展。
  • 依赖注入:内置依赖注入系统,简化了组件之间的依赖管理。
  • 装饰器支持:利用 TypeScript 装饰器,使代码更加简洁易读。
  • TypeScript 集成:完全支持 TypeScript,提供类型安全和更好的开发体验。
  • GraphQL 支持:内置对 GraphQL 的支持,便于构建现代 API。
  • 微服务架构:支持构建微服务应用,适应现代云原生架构。
  • 测试友好:内置测试工具和最佳实践,便于编写单元测试和集成测试。

安装与配置

安装 NestJS CLI

NestJS 提供了命令行工具,用于快速创建和管理项目:

# 全局安装 NestJS CLI
npm install -g @nestjs/cli

# 检查安装是否成功
nest --version

创建第一个 NestJS 应用

使用 NestJS CLI 创建新项目:

# 创建新项目
nest new nestjs-demo

# 进入项目目录
cd nestjs-demo

# 启动开发服务器
npm run start:dev

项目结构

创建的 NestJS 项目默认包含以下结构:

├── src/
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test/
├── nest-cli.json
├── package.json
├── tsconfig.json
└── README.md
  • main.ts:应用程序入口文件,负责启动 NestJS 应用。
  • app.module.ts:根模块,组织和管理其他模块。
  • app.controller.ts:处理 HTTP 请求,定义路由。
  • app.service.ts:业务逻辑层,处理具体的业务逻辑。

核心概念

模块 (Modules)

模块是 NestJS 应用的基本组织单位,用于组织代码和管理依赖。

// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

控制器 (Controllers)

控制器负责处理 HTTP 请求,定义路由和请求处理逻辑。

// cats.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
    return this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: string): Promise<Cat> {
    return this.catsService.findOne(id);
  }
}

提供者 (Providers)

提供者是 NestJS 中可以被注入的对象,如服务、仓库、工厂等,用于处理业务逻辑。

// cats.service.ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
import { CreateCatDto } from './dto/create-cat.dto';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: CreateCatDto): Cat {
    this.cats.push(cat);
    return cat;
  }

  findAll(): Cat[] {
    return this.cats;
  }

  findOne(id: string): Cat {
    return this.cats.find(cat => cat.id === id);
  }
}

数据传输对象 (DTOs)

DTOs 用于定义数据传输的结构,确保数据的有效性和一致性。

// create-cat.dto.ts
export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}

中间件 (Middleware)

中间件是处理请求和响应的函数,可以在路由处理之前或之后执行。

// logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
    next();
  }
}

异常过滤器 (Exception Filters)

异常过滤器用于捕获和处理应用中的异常,提供统一的错误处理机制。

// http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response
      .status(status)
      .json({
        statusCode: status,
        timestamp: new Date().toISOString(),
        path: request.url,
        message: exception.message,
      });
  }
}

实用案例分析

构建 RESTful API

下面是一个使用 NestJS 构建 RESTful API 的示例,实现了基本的 CRUD 操作。

项目结构

├── src/
│   ├── cats/
│   │   ├── dto/
│   │   │   ├── create-cat.dto.ts
│   │   │   └── update-cat.dto.ts
│   │   ├── interfaces/
│   │   │   └── cat.interface.ts
│   │   ├── cats.controller.ts
│   │   ├── cats.service.ts
│   │   └── cats.module.ts
│   ├── app.module.ts
│   └── main.ts
└── package.json

代码实现

  1. 定义接口 (cats/interfaces/cat.interface.ts):
export interface Cat {
  id: string;
  name: string;
  age: number;
  breed: string;
}
  1. 定义 DTOs:
// cats/dto/create-cat.dto.ts
export class CreateCatDto {
  readonly name: string;
  readonly age: number;
  readonly breed: string;
}

// cats/dto/update-cat.dto.ts
export class UpdateCatDto {
  readonly name?: string;
  readonly age?: number;
  readonly breed?: string;
}
  1. 实现服务 (cats/cats.service.ts):
import { Injectable, NotFoundException } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { v4 as uuidv4 } from 'uuid';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(createCatDto: CreateCatDto): Cat {
    const cat: Cat = {
      id: uuidv4(),
      ...createCatDto,
    };
    this.cats.push(cat);
    return cat;
  }

  findAll(): Cat[] {
    return this.cats;
  }

  findOne(id: string): Cat {
    const cat = this.cats.find(cat => cat.id === id);
    if (!cat) {
      throw new NotFoundException(`Cat with id ${id} not found`);
    }
    return cat;
  }

  update(id: string, updateCatDto: UpdateCatDto): Cat {
    const cat = this.findOne(id);
    const index = this.cats.findIndex(c => c.id === id);
    this.cats[index] = { ...cat, ...updateCatDto };
    return this.cats[index];
  }

  remove(id: string): void {
    const index = this.cats.findIndex(cat => cat.id === id);
    if (index === -1) {
      throw new NotFoundException(`Cat with id ${id} not found`);
    }
    this.cats.splice(index, 1);
  }
}
  1. 实现控制器 (cats/cats.controller.ts):
import { Controller, Get, Post, Put, Delete, Body, Param, HttpCode, HttpStatus } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  @HttpCode(HttpStatus.CREATED)
  create(@Body() createCatDto: CreateCatDto): Cat {
    return this.catsService.create(createCatDto);
  }

  @Get()
  findAll(): Cat[] {
    return this.catsService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): Cat {
    return this.catsService.findOne(id);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto): Cat {
    return this.catsService.update(id, updateCatDto);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id') id: string): void {
    this.catsService.remove(id);
  }
}
  1. 创建模块 (cats/cats.module.ts):
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}
  1. 更新根模块 (app.module.ts):
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}
  1. 启动应用 (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);
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();

测试 API

使用 curl 或 Postman 测试 API:

# 创建猫
curl -X POST http://localhost:3000/cats -H "Content-Type: application/json" -d '{"name": "Tom", "age": 3, "breed": "Siamese"}'

# 获取所有猫
curl http://localhost:3000/cats

# 获取单个猫
curl http://localhost:3000/cats/{id}

# 更新猫
curl -X PUT http://localhost:3000/cats/{id} -H "Content-Type: application/json" -d '{"age": 4}'

# 删除猫
curl -X DELETE http://localhost:3000/cats/{id}

数据库集成

NestJS 支持多种数据库,包括 MongoDB、PostgreSQL、MySQL 等。下面以 MongoDB 为例,展示如何集成数据库。

安装依赖

# 安装 Mongoose 和相关依赖
npm install mongoose @nestjs/mongoose

配置数据库连接

// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost:27017/nestjs-demo'),
    CatsModule,
  ],
})
export class AppModule {}

定义数据库模型

// cats/schemas/cat.schema.ts
import { Schema } from 'mongoose';

export const CatSchema = new Schema({
  name: String,
  age: Number,
  breed: String,
});

更新模块配置

// cats/cats.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { CatSchema } from './schemas/cat.schema';

@Module({
  imports: [MongooseModule.forFeature([{ name: 'Cat', schema: CatSchema }])],
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

更新服务实现

import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Cat } from './interfaces/cat.interface';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';

@Injectable()
export class CatsService {
  constructor(@InjectModel('Cat') private readonly catModel: Model<Cat>) {}

  async create(createCatDto: CreateCatDto): Promise<Cat> {
    const createdCat = new this.catModel(createCatDto);
    return await createdCat.save();
  }

  async findAll(): Promise<Cat[]> {
    return await this.catModel.find().exec();
  }

  async findOne(id: string): Promise<Cat> {
    const cat = await this.catModel.findById(id).exec();
    if (!cat) {
      throw new NotFoundException(`Cat with id ${id} not found`);
    }
    return cat;
  }

  async update(id: string, updateCatDto: UpdateCatDto): Promise<Cat> {
    const updatedCat = await this.catModel.findByIdAndUpdate(id, updateCatDto, { new: true }).exec();
    if (!updatedCat) {
      throw new NotFoundException(`Cat with id ${id} not found`);
    }
    return updatedCat;
  }

  async remove(id: string): Promise<void> {
    const result = await this.catModel.findByIdAndDelete(id).exec();
    if (!result) {
      throw new NotFoundException(`Cat with id ${id} not found`);
    }
  }
}

总结

NestJS 是一个现代化的 Node.js 框架,它结合了多种编程范式的最佳实践,为开发者提供了一种结构化、可扩展的方式来构建后端应用。通过本教程,你应该已经了解了 NestJS 的核心概念和基本用法,包括模块、控制器、提供者、DTOs、中间件、异常过滤器等。

NestJS 的模块化设计和依赖注入系统,使得代码组织更加清晰,便于维护和扩展。它对 TypeScript 的完全支持,提供了类型安全和更好的开发体验。此外,NestJS 还内置了对 GraphQL、微服务等现代技术的支持,适应了现代应用开发的需求。

要深入学习 NestJS,建议查阅 官方文档 和实践更多的项目案例,以掌握其高级特性和最佳实践。NestJS 的生态系统也在不断发展,有大量的第三方库和工具可供选择,可以进一步提高开发效率。

« 上一篇 Express.js 教程 下一篇 » Fastify 教程