后端
2025年7月1日
14 分钟阅读
Nest.js 入门指南:构建现代 Node.js 应用
Nest.js 入门指南:构建现代 Node.js 应用
Nest.js 是一个用于构建高效、可扩展的 Node.js 服务器端应用的框架。它使用 TypeScript 构建,结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数响应式编程)的元素。本文将从基础概念开始,带你快速入门 Nest.js。
什么是 Nest.js?
核心特性
Nest.js 是一个渐进式的 Node.js 框架,具有以下特点:
- 基于 TypeScript:提供类型安全和更好的开发体验
- 模块化架构:借鉴 Angular 的模块化设计
- 依赖注入:内置强大的依赖注入系统
- 装饰器支持:大量使用装饰器简化代码
- 开箱即用:内置对 TypeORM、MongoDB、GraphQL 等的支持
为什么选择 Nest.js?
相比 Express 等基础框架,Nest.js 提供了:
- 结构化开发:强制性的项目结构和最佳实践
- 可扩展性:模块化设计便于团队协作和项目扩展
- 企业级特性:内置测试、验证、日志等功能
- TypeScript 支持:完整的类型系统支持
项目初始化
安装 Nest.js CLI
# 全局安装 Nest.js CLI
npm i -g @nestjs/cli
# 创建新项目
nest new project-name
# 或使用 npm/yarn
npm i -g @nestjs/cli
yarn global add @nestjs/cli项目结构
创建项目后,典型的目录结构如下:
src/
main.ts # 应用入口文件
app.module.ts # 根模块
app.controller.ts # 根控制器
app.service.ts # 根服务
启动应用
# 开发模式(热重载)
npm run start:dev
# 生产模式
npm run build
npm run start:prod应用默认运行在 http://localhost:3000。
核心概念:模块(Module)
什么是模块?
模块是 Nest.js 应用的基本组织单元,使用 @Module() 装饰器定义:
// app.module.ts
import { Module } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
@Module({
imports: [], // 导入其他模块
controllers: [AppController], // 控制器
providers: [AppService], // 提供者(服务等)
exports: [], // 导出供其他模块使用
})
export class AppModule {}功能模块示例
创建用户模块:
// users/users.module.ts
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService], // 导出供其他模块使用
})
export class UsersModule {}模块导入
在根模块中导入功能模块:
// app.module.ts
import { Module } from "@nestjs/common";
import { UsersModule } from "./users/users.module";
@Module({
imports: [UsersModule], // 导入用户模块
controllers: [],
providers: [],
})
export class AppModule {}核心概念:控制器(Controller)
什么是控制器?
控制器负责处理 HTTP 请求并返回响应,使用 @Controller() 装饰器定义:
// users/users.controller.ts
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
import { UsersService } from "./users.service";
@Controller("users") // 路由前缀 /users
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get() // GET /users
findAll() {
return this.usersService.findAll();
}
@Get(":id") // GET /users/:id
findOne(@Param("id") id: string) {
return this.usersService.findOne(id);
}
@Post() // POST /users
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}常用装饰器
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
Headers,
HttpCode,
HttpStatus,
} from "@nestjs/common";
@Controller("products")
export class ProductsController {
@Get()
findAll(@Query("page") page: number) {
// GET /products?page=1
return { page };
}
@Post()
@HttpCode(HttpStatus.CREATED) // 自定义状态码
create(@Body() createProductDto: CreateProductDto) {
return this.productsService.create(createProductDto);
}
@Get(":id")
findOne(@Param("id") id: string) {
// GET /products/:id
return this.productsService.findOne(id);
}
@Put(":id")
update(@Param("id") id: string, @Body() updateProductDto: UpdateProductDto) {
return this.productsService.update(id, updateProductDto);
}
@Delete(":id")
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param("id") id: string) {
return this.productsService.remove(id);
}
}路由参数
@Controller("blog")
export class BlogController {
// 单个参数
@Get(":id")
findOne(@Param("id") id: string) {
return { id };
}
// 多个参数
@Get(":category/:slug")
findByCategoryAndSlug(
@Param("category") category: string,
@Param("slug") slug: string
) {
return { category, slug };
}
// 获取所有参数
@Get(":id")
findOneAllParams(@Param() params: { id: string }) {
return params;
}
}核心概念:服务(Service)
什么是服务?
服务包含业务逻辑,使用 @Injectable() 装饰器定义,可以被注入到控制器或其他服务中:
// users/users.service.ts
import { Injectable } from "@nestjs/common";
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: "John", email: "john@example.com" },
{ id: 2, name: "Jane", email: "jane@example.com" },
];
findAll() {
return this.users;
}
findOne(id: string) {
return this.users.find((user) => user.id === parseInt(id));
}
create(createUserDto: CreateUserDto) {
const newUser = {
id: this.users.length + 1,
...createUserDto,
};
this.users.push(newUser);
return newUser;
}
}服务依赖注入
在控制器中注入服务:
// users/users.controller.ts
import { Controller, Get } from "@nestjs/common";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
}注意:Nest.js 使用构造函数注入,通过 private readonly 语法简化代码。
DTO(Data Transfer Object)
什么是 DTO?
DTO 用于定义数据传输对象的结构和验证规则:
// users/dto/create-user.dto.ts
import { IsString, IsEmail, IsNotEmpty, MinLength } from "class-validator";
export class CreateUserDto {
@IsNotEmpty()
@IsString()
name: string;
@IsNotEmpty()
@IsEmail()
email: string;
@IsNotEmpty()
@MinLength(6)
password: string;
}使用 DTO
// users/users.controller.ts
import { Controller, Post, Body } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UsersService } from "./users.service";
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
}完整的 CRUD 示例
创建资源模块
# 使用 CLI 创建完整资源
nest g resource products这会自动生成:
products.module.ts- 模块文件products.controller.ts- 控制器文件products.service.ts- 服务文件dto/- DTO 目录
完整示例
// products/products.service.ts
import { Injectable, NotFoundException } from "@nestjs/common";
import { CreateProductDto } from "./dto/create-product.dto";
import { UpdateProductDto } from "./dto/update-product.dto";
@Injectable()
export class ProductsService {
private products = [
{ id: 1, name: "Product 1", price: 100 },
{ id: 2, name: "Product 2", price: 200 },
];
create(createProductDto: CreateProductDto) {
const newProduct = {
id: this.products.length + 1,
...createProductDto,
};
this.products.push(newProduct);
return newProduct;
}
findAll() {
return this.products;
}
findOne(id: number) {
const product = this.products.find((p) => p.id === id);
if (!product) {
throw new NotFoundException(`Product with ID ${id} not found`);
}
return product;
}
update(id: number, updateProductDto: UpdateProductDto) {
const product = this.findOne(id);
Object.assign(product, updateProductDto);
return product;
}
remove(id: number) {
const index = this.products.findIndex((p) => p.id === id);
if (index === -1) {
throw new NotFoundException(`Product with ID ${id} not found`);
}
this.products.splice(index, 1);
return { message: "Product deleted successfully" };
}
}// products/products.controller.ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
ParseIntPipe,
} from "@nestjs/common";
import { ProductsService } from "./products.service";
import { CreateProductDto } from "./dto/create-product.dto";
import { UpdateProductDto } from "./dto/update-product.dto";
@Controller("products")
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
@Post()
create(@Body() createProductDto: CreateProductDto) {
return this.productsService.create(createProductDto);
}
@Get()
findAll() {
return this.productsService.findAll();
}
@Get(":id")
findOne(@Param("id", ParseIntPipe) id: number) {
return this.productsService.findOne(id);
}
@Patch(":id")
update(
@Param("id", ParseIntPipe) id: number,
@Body() updateProductDto: UpdateProductDto
) {
return this.productsService.update(id, updateProductDto);
}
@Delete(":id")
remove(@Param("id", ParseIntPipe) id: number) {
return this.productsService.remove(id);
}
}管道(Pipe):数据验证和转换
内置管道
import { ParseIntPipe, ParseBoolPipe, DefaultValuePipe } from '@nestjs/common';
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
// id 自动转换为 number 类型
return this.productsService.findOne(id);
}
@Get('active')
findActive(@Query('active', new DefaultValuePipe(true), ParseBoolPipe) active: boolean) {
return this.productsService.findActive(active);
}自定义验证管道
// common/pipes/validation.pipe.ts
import {
PipeTransform,
ArgumentMetadata,
BadRequestException,
} from "@nestjs/common";
import { validate } from "class-validator";
import { plainToInstance } from "class-transformer";
export class ValidationPipe implements PipeTransform {
async transform(value: any, { metatype }: ArgumentMetadata) {
if (!metatype || !this.toValidate(metatype)) {
return value;
}
const object = plainToInstance(metatype, value);
const errors = await validate(object);
if (errors.length > 0) {
throw new BadRequestException("Validation failed");
}
return value;
}
private toValidate(metatype: Function): boolean {
const types: Function[] = [String, Boolean, Number, Array, Object];
return !types.includes(metatype);
}
}异常处理
内置异常
import {
NotFoundException,
BadRequestException,
UnauthorizedException,
ForbiddenException,
ConflictException,
} from '@nestjs/common';
@Get(':id')
findOne(@Param('id') id: string) {
const product = this.productsService.findOne(id);
if (!product) {
throw new NotFoundException(`Product with ID ${id} not found`);
}
return product;
}自定义异常过滤器
// common/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
完整的用户管理模块
// users/users.module.ts
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller";
import { UsersService } from "./users.service";
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}// users/dto/create-user.dto.ts
export class CreateUserDto {
name: string;
email: string;
password: string;
}
// users/dto/update-user.dto.ts
export class UpdateUserDto {
name?: string;
email?: string;
}// users/users.service.ts
import { Injectable } from "@nestjs/common";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
@Injectable()
export class UsersService {
private users = [];
create(createUserDto: CreateUserDto) {
const user = { id: Date.now(), ...createUserDto };
this.users.push(user);
return user;
}
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find((user) => user.id === id);
}
update(id: number, updateUserDto: UpdateUserDto) {
const user = this.findOne(id);
if (user) {
Object.assign(user, updateUserDto);
}
return user;
}
remove(id: number) {
const index = this.users.findIndex((user) => user.id === id);
if (index > -1) {
this.users.splice(index, 1);
return { message: "User deleted" };
}
}
}最佳实践
- 模块化设计:每个功能创建一个独立模块
- 使用 DTO:定义清晰的数据传输对象
- 依赖注入:充分利用 Nest.js 的依赖注入系统
- 异常处理:使用适当的异常类型和异常过滤器
- 管道验证:使用管道进行数据验证和转换
- 单一职责:控制器处理请求,服务包含业务逻辑
- 使用 CLI:充分利用 Nest CLI 提高开发效率
通过本文的学习,你已经掌握了 Nest.js 的核心概念。接下来可以深入学习依赖注入、中间件、守卫等高级特性,构建更复杂的应用。