下面是一个完整的 NestJS RESTful API 示例,实现一个订单管理系统(包括 CRUD 操作),涵盖最佳实践如 DTO 验证服务层逻辑异常处理Swagger 文档
在这里插入图片描述


在这里插入图片描述

项目结构

src/
├── order/
│   ├── dto/
│   │   ├── create-order.dto.ts    # 创建订单的验证规则
│   │   ├── update-order.dto.ts    # 更新订单的验证规则
│   │   └── order-response.dto.ts  # 统一响应格式
│   ├── entities/
│   │   └── order.entity.ts        # 订单实体类
│   ├── order.controller.ts        # 控制器(路由定义)
│   ├── order.service.ts           # 服务层(业务逻辑)
│   └── order.module.ts            # 模块定义
├── app.module.ts                  # 根模块
└── main.ts                        # 入口文件(启用Swagger)

在这里插入图片描述

代码实现

1. 订单实体 (order.entity.ts)
export class Order {
  id: number;
  productName: string;
  quantity: number;
  price: number;
  status: 'PENDING' | 'SHIPPED' | 'DELIVERED'; // 订单状态枚举
  createdAt: Date;
}
2. DTO 数据传输对象

创建订单 DTO (create-order.dto.ts):

import { IsString, IsInt, Min, IsEnum } from 'class-validator';

export class CreateOrderDto {
  @IsString()
  productName: string;

  @IsInt()
  @Min(1)
  quantity: number;

  @IsInt()
  @Min(0)
  price: number;

  @IsEnum(['PENDING', 'SHIPPED'])
  status: 'PENDING' | 'SHIPPED';
}

更新订单 DTO (update-order.dto.ts):

import { PartialType } from '@nestjs/mapped-types';
import { CreateOrderDto } from './create-order.dto';

// 继承CreateOrderDto并设为可选
export class UpdateOrderDto extends PartialType(CreateOrderDto) {}
3. 服务层 (order.service.ts)
import { Injectable, NotFoundException } from '@nestjs/common';
import { Order } from './entities/order.entity';
import { CreateOrderDto, UpdateOrderDto } from './dto';

@Injectable()
export class OrderService {
  private orders: Order[] = []; // 模拟数据库
  private nextId = 1;

  // 创建订单
  create(createOrderDto: CreateOrderDto): Order {
    const newOrder: Order = {
      id: this.nextId++,
      ...createOrderDto,
      status: createOrderDto.status || 'PENDING',
      createdAt: new Date(),
    };
    this.orders.push(newOrder);
    return newOrder;
  }

  // 查找所有订单
  findAll(): Order[] {
    return this.orders;
  }

  // 查找单个订单
  findOne(id: number): Order {
    const order = this.orders.find(o => o.id === id);
    if (!order) throw new NotFoundException(`Order #${id} not found`);
    return order;
  }

  // 更新订单
  update(id: number, updateOrderDto: UpdateOrderDto): Order {
    const order = this.findOne(id);
    Object.assign(order, updateOrderDto);
    return order;
  }

  // 删除订单
  remove(id: number): void {
    const index = this.orders.findIndex(o => o.id === id);
    if (index === -1) throw new NotFoundException(`Order #${id} not found`);
    this.orders.splice(index, 1);
  }
}
4. 控制器 (order.controller.ts)
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';
import { OrderService } from './order.service';
import { CreateOrderDto, UpdateOrderDto } from './dto';
import { Order } from './entities/order.entity';

@Controller('orders') // 基础路径 /orders
export class OrderController {
  constructor(private readonly orderService: OrderService) {}

  @Post()
  @HttpCode(HttpStatus.CREATED)
  create(@Body() createOrderDto: CreateOrderDto): Order {
    return this.orderService.create(createOrderDto);
  }

  @Get()
  findAll(): Order[] {
    return this.orderService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): Order {
    return this.orderService.findOne(+id); // 转换为数字
  }

  @Put(':id')
  update(
    @Param('id') id: string,
    @Body() updateOrderDto: UpdateOrderDto,
  ): Order {
    return this.orderService.update(+id, updateOrderDto);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id') id: string): void {
    this.orderService.remove(+id);
  }
}
5. 模块定义 (order.module.ts)
import { Module } from '@nestjs/common';
import { OrderController } from './order.controller';
import { OrderService } from './order.service';

@Module({
  controllers: [OrderController],
  providers: [OrderService],
})
export class OrderModule {}
6. 启用 Swagger 文档 (main.ts)
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // 配置Swagger
  const config = new DocumentBuilder()
    .setTitle('Order Management API')
    .setDescription('The NestJS Order API description')
    .setVersion('1.0')
    .addTag('orders')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document); // 访问 /api 查看文档

  await app.listen(3000);
}
bootstrap();

在这里插入图片描述

测试 API(使用 curl)

创建订单
curl -X POST http://localhost:3000/orders \
  -H "Content-Type: application/json" \
  -d '{
    "productName": "Laptop",
    "quantity": 2,
    "price": 1200,
    "status": "PENDING"
  }'
响应
{
  "id": 1,
  "productName": "Laptop",
  "quantity": 2,
  "price": 1200,
  "status": "PENDING",
  "createdAt": "2023-10-05T08:30:00.000Z"
}
获取所有订单
curl http://localhost:3000/orders
更新订单
curl -X PUT http://localhost:3000/orders/1 \
  -H "Content-Type: application/json" \
  -d '{"status": "SHIPPED"}'
删除订单
curl -X DELETE http://localhost:3000/orders/1

关键特性演示

  1. DTO 验证
    如果请求缺少必填字段(如 price),自动返回 400 Bad Request

    {
      "statusCode": 400,
      "message": ["price must be a positive number"],
      "error": "Bad Request"
    }
    
  2. 异常处理
    访问不存在的订单 ID 时返回 404 Not Found

    {
      "statusCode": 404,
      "message": "Order #99 not found",
      "error": "Not Found"
    }
    
  3. Swagger 文档
    访问 http://localhost:3000/api 查看交互式文档:
    !https://miro.medium.com/v2/resize:fit:1400/1*5J6zWlVvYqQfXqj_9y6x3g.png


进阶优化建议

  1. 数据库集成
    替换内存存储为 TypeORM/Prisma:

    // 使用TypeORM实体
    @Entity()
    export class OrderEntity {
      @PrimaryGeneratedColumn()
      id: number;
      
      @Column()
      productName: string;
      // ...
    }
    
  2. 分页查询
    在服务层添加分页参数:

    findAll(page = 1, limit = 10) {
      return this.orders.slice((page - 1) * limit, page * limit);
    }
    
  3. 身份验证
    添加 JWT 守卫保护敏感操作:

    @UseGuards(JwtAuthGuard)
    @Delete(':id')
    remove(@Param('id') id: string) { ... }
    

这个示例展示了 NestJS 的核心优势:清晰的代码结构强类型验证可扩展性开发者友好的工具链。实际项目中可根据需求扩展功能(如数据库、缓存、消息队列)。


RESTful API

RESTful API 是一种基于 REST(Representational State Transfer,表述性状态转移)架构风格 设计的 API,核心思想是通过 资源(Resource) 为中心,利用 HTTP 协议的标准方法(GET/POST/PUT/DELETE 等)实现对资源的操作,具有简洁、可扩展、松耦合的特点。以下是其基础知识体系:

在这里插入图片描述

一、REST 的核心约束(6大原则)

REST 架构定义了 6 条约束(其中第 6 条为可选),满足这些约束的 API 才能称为“RESTful”:

约束 含义
1. 客户端-服务器分离 客户端(如浏览器、App)负责用户界面,服务器负责数据存储和业务逻辑,两者独立演化。
2. 无状态(Stateless) 服务器不存储客户端上下文(如会话信息),每次请求必须包含所有必要信息(如认证令牌),简化服务器设计。
3. 可缓存(Cacheable) 响应需明确是否可缓存(通过 Cache-Control 头),减少重复请求,提升性能。
4. 统一接口(Uniform Interface) 客户端与服务器通过标准化的接口通信,核心包括:
- 资源通过 URI 唯一标识;
- 通过 HTTP 方法操作资源;
- 资源表述自包含(如 JSON/XML);
- HATEOAS(超媒体驱动,见下文)。
5. 分层系统(Layered System) 架构可分为多层(如负载均衡层、应用层、数据层),每层仅与相邻层交互,隐藏内部实现。
6. 按需代码(Code on Demand,可选) 服务器可向客户端临时发送可执行代码(如 JavaScript),扩展客户端功能(较少使用)。

二、RESTful API 的关键特征

1. 资源导向(Resource-Oriented)
  • 资源:API 操作的核心对象(如用户、订单、商品),用 URI(统一资源标识符) 唯一标识。

  • URI 设计原则

    • 名词复数 表示资源集合(如 /users 表示用户列表,/orders 表示订单列表);
    • 嵌套 URI 表示资源关系(如 /users/123/orders 表示 ID=123 用户的订单);
    • 避免动词(如不用 /getUsers,而用 GET /users);
    • 小写字母+连字符分隔(如 /user-profiles,不用驼峰)。

    ✅ 正确示例:GET /orders/456(获取 ID=456 的订单)、POST /products(创建商品)。
    ❌ 错误示例:GET /getOrder?id=456(含动词)、POST /createProduct(冗余动词)。

2. HTTP 方法映射 CRUD 操作

RESTful API 严格遵循 HTTP 方法的语义,对应资源的 增删改查(CRUD)

HTTP 方法 语义 操作 示例 URI 响应状态码
GET 获取资源(幂等) 查询单个/多个资源 GET /orders(所有订单)
GET /orders/1(ID=1 的订单)
200 OK(成功)
404 Not Found(资源不存在)
POST 创建资源(非幂等) 新增资源(服务器生成 ID) POST /orders(创建订单) 201 Created(成功创建)
400 Bad Request(参数错误)
PUT 全量更新资源(幂等) 替换整个资源(需提供完整字段) PUT /orders/1(更新 ID=1 的订单,全量替换) 200 OK(成功)
404 Not Found(资源不存在)
PATCH 部分更新资源(非幂等) 仅修改部分字段(如只改状态) PATCH /orders/1(更新 ID=1 的订单状态) 200 OK(成功)
DELETE 删除资源(幂等) 删除指定资源 DELETE /orders/1(删除 ID=1 的订单) 204 No Content(成功删除,无响应体)
  • 幂等性:多次执行同一请求,结果相同(如 GET/PUT/DELETE 是幂等的,POST 不是)。
3. 状态码(Status Code)

用 HTTP 标准状态码表示请求结果,避免自定义错误码:

类别 状态码 含义 示例场景
2xx 成功 200 OK 请求成功(GET/PUT/PATCH) 获取订单列表、更新订单成功
201 Created 资源创建成功(POST) 创建订单后返回新订单 ID
204 No Content 成功但无响应体(DELETE) 删除订单后返回空响应
4xx 客户端错误 400 Bad Request 请求参数错误(如格式不对) 创建订单时 price 为负数
401 Unauthorized 未认证(如 token 无效) 未登录用户访问需认证的接口
403 Forbidden 已认证但无权限 普通用户尝试删除他人订单
404 Not Found 资源不存在 访问 /orders/999(ID=999 的订单不存在)
409 Conflict 资源冲突(如重复创建) 创建已存在的用户名
5xx 服务器错误 500 Internal Server Error 服务器内部错误(如代码 bug) 数据库连接失败
4. 数据格式与表述
  • 表述(Representation):资源的具体形式(如 JSON、XML),最常用 JSON(轻量、易读)。
  • 请求/响应头
    • Content-Type: application/json:表示请求体/响应体是 JSON 格式;
    • Accept: application/json:客户端希望接收 JSON 格式响应。
  • 示例响应体(JSON)
    {
      "id": 1,
      "productName": "Laptop",
      "quantity": 2,
      "price": 1200,
      "status": "PENDING",
      "createdAt": "2023-10-05T08:30:00Z",
      "_links": {  // HATEOAS 链接(可选)
        "self": { "href": "/orders/1" },
        "update": { "href": "/orders/1", "method": "PUT" }
      }
    }
    
5. HATEOAS(超媒体作为应用状态的引擎)

REST 的高级特性:响应中不仅包含数据,还包含 超媒体链接,引导客户端下一步操作(类似网页的“超链接”)。

  • 例如,获取订单列表时,响应中可包含“创建订单”“查看订单详情”的链接,客户端无需硬编码 URI。
  • 目前多数 API 未完全实现,但体现了 REST 的“自描述”思想。
6. 版本控制

API 迭代时需保证旧版本兼容,常见版本控制方式:

  • URI 版本/v1/orders/v2/orders(直观,最常用);
  • 请求头版本Accept: application/vnd.company.v1+json(通过 Accept 头指定版本);
  • 查询参数版本/orders?version=1(不推荐,破坏 URI 纯净性)。

三、RESTful API 最佳实践

  1. 使用 HTTPS:加密传输数据,避免明文暴露。
  2. 合理分页/过滤:对大数据集用 ?page=1&limit=10 分页,?status=PENDING 过滤。
  3. 统一错误处理:用标准 JSON 格式返回错误(如 { "code": "ORDER_NOT_FOUND", "message": "订单不存在" })。
  4. 认证与授权:用 JWT/OAuth2 等机制,通过 Authorization: Bearer <token> 头传递凭证。
  5. 避免过度设计:小型 API 无需严格遵循所有约束(如 HATEOAS),优先保证简洁可用。

四、RESTful API vs 其他 API 风格

对比项 RESTful API GraphQL SOAP
核心思想 资源导向,HTTP 方法语义 按需获取数据(单一端点) XML 协议,强类型契约(WSDL)
灵活性 固定端点,多请求获取关联数据 单端点,客户端自定义查询字段 严格契约,灵活性低
性能 可能产生冗余数据(Over-fetching) 精确获取数据(Under-fetching) 较重(XML 冗余),性能较差
适用场景 简单 CRUD、通用后端 API 复杂前端需求(如动态表单) 企业级系统集成(银行、政府)

五、总结

RESTful API 的核心是 “资源为中心 + HTTP 语义标准化”,通过简洁的 URI、明确的 HTTP 方法和状态码,实现客户端与服务器的松耦合通信。其优势在于 易理解、易扩展、兼容性好,是现代 Web 开发中最主流的 API 设计风格(如 GitHub API、Twitter API 均采用 RESTful 设计)。

结合前文 NestJS 示例,其控制器(@Controller('/orders'))和 HTTP 方法装饰器(@Get/@Post 等)正是 RESTful 思想的落地实现,通过模块化分层进一步强化了 API 的可维护性。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐