AI Agent 开发学习路线-第五课

0 阅读2分钟

1 Nestjs 写get post put delete 接口

NestJS 实战:CRUD 四个接口

一、整体认知:NestJS 的三层分工

写一个接口涉及三个角色: 请求进来 → Controller(接收请求、返回响应) ↓ 调用 Service(写业务逻辑) ↓ 操作 数据(数据库/数组)

  • Controller:只管"接"和"回",不写逻辑
  • Service:逻辑都写这里,通过依赖注入给 Controller 用
  • DTO:定义请求体的形状(等价于 FastAPI 里的 Pydantic 模型)

二、完整代码:用户管理 CRUD

1. DTO(数据校验)—— src/users/dto/user.dto.ts

TypeScript

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

// 创建用户时的请求体
export class CreateUserDto {
  @IsString()
  name: string;

  @IsInt()
  @Min(1)
  @Max(150)
  age: number;
}

// 更新用户时(字段都可选,改哪个传哪个)
export class UpdateUserDto {
  @IsOptional()
  @IsString()
  name?: string;

  @IsOptional()
  @IsInt()
  age?: number;
}

需要 npm install class-validator class-transformer,并在 main.ts 里开启全局校验:

TypeScript

app.useGlobalPipes(new ValidationPipe());

2. Service(业务逻辑)—— src/users/users.service.ts

TypeScript

import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';

interface User {
  id: number;
  name: string;
  age: number;
}

@Injectable()
export class UsersService {
  // 先用内存数组模拟数据库(学到 TypeORM/Prisma 再换真的)
  private users: User[] = [
    { id: 1, name: '张三', age: 25 },
    { id: 2, name: '李四', age: 30 },
  ];
  private nextId = 3;

  findAll(): User[] {
    return this.users;
  }

  findOne(id: number): User {
    const user = this.users.find((u) => u.id === id);
    if (!user) {
      throw new NotFoundException(`用户 ${id} 不存在`);  // 自动返回 404
    }
    return user;
  }

  create(dto: CreateUserDto): User {
    const user: User = { id: this.nextId++, ...dto };
    this.users.push(user);
    return user;
  }

  update(id: number, dto: UpdateUserDto): User {
    const user = this.findOne(id);      // 复用,找不到会自动抛 404
    Object.assign(user, dto);
    return user;
  }

  remove(id: number): void {
    const user = this.findOne(id);
    this.users = this.users.filter((u) => u.id !== user.id);
  }
}

3. Controller(路由层)—— src/users/users.controller.ts

TypeScript

import {
  Controller, Get, Post, Put, Delete,
  Param, Body, ParseIntPipe,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto, UpdateUserDto } from './dto/user.dto';

@Controller('users')   // 所有路由都以 /users 开头
export class UsersController {
  // 依赖注入:NestJS 自动把 Service 实例塞进来
  constructor(private readonly usersService: UsersService) {}

  @Get()                          // GET /users        → 查全部
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')                     // GET /users/1      → 查单个
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.usersService.findOne(id);
  }

  @Post()                         // POST /users       → 新增
  create(@Body() dto: CreateUserDto) {
    return this.usersService.create(dto);
  }

  @Put(':id')                     // PUT /users/1      → 更新
  update(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateUserDto,
  ) {
    return this.usersService.update(id, dto);
  }

  @Delete(':id')                  // DELETE /users/1   → 删除
  remove(@Param('id', ParseIntPipe) id: number) {
    this.usersService.remove(id);
    return { message: `用户 ${id} 已删除` };
  }
}

4. 注册到 Module —— src/users/users.module.ts

TypeScript

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

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

然后在 app.module.tsimports 数组里加上 UsersModule,启动:

powershell

npm run start:dev

三、测试(PowerShell 里用 curl 验证)

powershell

# 查全部
curl http://localhost:3000/users

# 新增
curl -X POST http://localhost:3000/users -H "Content-Type: application/json" -d '{"name":"王五","age":28}'

# 更新
curl -X PUT http://localhost:3000/users/1 -H "Content-Type: application/json" -d '{"age":26}'

# 删除
curl -X DELETE http://localhost:3000/users/1

试试故意传错数据(比如 age 传 200),会被 DTO 校验拦住,自动返回 400——这就是 class-validator 的价值。

四、和 FastAPI 对照(提前建立直觉)

表格

概念NestJSFastAPI(第 06 章会学)
路由声明@Get(':id') 装饰器@app.get("/{id}") 装饰器
路径参数@Param('id', ParseIntPipe)id: int(自动转换)
请求体校验DTO + class-validatorPydantic 模型
依赖注入constructor(private service)Depends()
业务分层Controller / ServiceRouter / Service

五、自测作业

在刚才的代码基础上加一个接口,不许看答案先自己写:

GET /users/search?keyword=张 —— 按名字模糊搜索用户(提示:用 @Query('keyword') 接收参数,filter + includes 过滤)