使用 NestJS 和 MongoDB(Mongoose)构建 RESTful API 简介 安装 安装 Nest CLI 创建新的 Nest 项目 在 IDE 中打开项目 创建“Todo”功能 创建 Todo 模型/模式 在 TodoService 中注入模型 定义 CRUD 功能 在 TodoController 中定义方法和路由端点 使用 REST 客户端进行测试 结论

2025-06-04

使用 NestJS 和 MongoDB(Mongoose)构建 RESTful API

介绍

安装

安装 Nest CLI

创建新的 Nest 项目

在 IDE 中打开项目

创建“Todo”功能

创建 Todo 模型/模式

在 TodoService 中注入模型

定义 CRUD 功能




在 TodoController 中定义方法和路由端点




使用 REST 客户端进行测试

结论

介绍

我们将学习如何使用 NestJS 框架为一个简单的待办事项应用程序实现 RESTful API。那么,NestJS 是什么呢?

“用于构建高效、可靠且可扩展的服务器端应用程序的渐进式 Node.js 框架。”

您可以在此处阅读有关 NestJS 的更多信息

本文假设您至少对 TypeScript 有基本的了解,对 NodeJS 和 ExpressJS 有更好的了解。但是,如果您不熟悉这些要求,我将列出我推荐您观看的内容以供学习:

我也推荐你订阅这些YouTube频道,因为它们内容质量很高,而且免费!我还有其他喜欢的YouTube频道,但我会在另一篇文章里分享。

如果你是一名前端开发人员,并且已经使用 Angular 一段时间了,那么你应该对 NestJS 非常熟悉,因为 NestJS 的代码结构与 Angular 非常相似!它支持依赖注入、模块、使用 CLI 生成代码等等!

安装

本安装指南将基于 Linux,因为我在 Windows 上使用 WSL2,这是我的偏好,并且觉得它更方便。我相信安装过程非常相似,但如果是其他平台,我建议您参考此处的文档。

安装 Nest CLI

打开终端并执行此命令来安装 Nest CLI



sudo npm install -g @nestjs/cli


Enter fullscreen mode Exit fullscreen mode

要测试它是否已成功安装,只需执行以下命令,它将告诉您当前为 Nest CLI 安装的版本



nest -v


Enter fullscreen mode Exit fullscreen mode

创建新的 Nest 项目

导航到您的项目目录或您喜欢的任何目录,然后运行以下命令来安装新项目



nest new todo-rest-app


Enter fullscreen mode Exit fullscreen mode

如果它询问您选择哪个包管理器,只需选择您喜欢的任何一个,但在本文中我将选择 NPM。

现在等待整个 CLI 为您搭建新的启动项目。

在 IDE 中打开项目

安装完成后,用你喜欢的代码编辑器打开它。我这里用的是 VSCode(Visual Studio Code),所以我会在终端里执行这个命令



cd todo-rest-app && code .


Enter fullscreen mode Exit fullscreen mode

然后就会打开你的 IDE。

创建“Todo”功能

我们可以使用强大的CLI轻松地生成Module类、Service类、Controller类的代码。

需要注意的是,创建新功能时,应该首先为该功能生成一个模块类。例如,TodoModule会先生成一个实例。

因此让我们立即生成它们!



# TodoModule
nest g module Todo
# Using alias: nest g mo Todo

# TodoService
nest g service Todo
# Using alias: nest g s Todo

# TodoController
nest g controller Todo 
# Using alias: nest g co Todo 


Enter fullscreen mode Exit fullscreen mode

这应该创建一个名为“todo”的文件夹,它还将在数组TodoService和数组中添加providersTodoModuleTodoControllercontrollers

创建 Todo 模型/模式

在编写处理数据并将其暴露给 REST API 的代码之前,我们首先要为 Todo 创建一个数据模型。因此,让我们使用 Mongoose 包创建一个模式,然后安装它。



npm install --save @nestjs/mongoose mongoose


Enter fullscreen mode Exit fullscreen mode

安装完成后,请确保将其添加MongooseModule到 imports 数组中。我们需要导入它,AppModule以便让应用程序知道 MongoDB 的来源。

但是,如果您的系统中没有安装 MongoDB,并且您使用的是基于 Linux 的系统,则可以将此作为参考



// app.module.ts

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/nest')],
})
export class AppModule {}


Enter fullscreen mode Exit fullscreen mode

MongooseModule.forRoot()添加后,AppModule我们就可以继续定义我们的 Todo 模式了,因此请转到“todo”目录,因为此功能目录已由 CLI 生成,因此在此目录下创建一个名为“schemas”的文件夹,它是 Todo 模式所在的位置

或者您可以使用这个终端命令来执行此操作



mkdir src/todo/schemas && touch src/todo/schemas/todo.schema.ts


Enter fullscreen mode Exit fullscreen mode

然后让我们定义我们的 Todo 模式



import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type TodoDocument = Todo & Document;

@Schema()
export class Todo {
  @Prop({ required: true })
  title: string;

  @Prop()
  description?: string;

  @Prop()
  completedAt?: Date;

  @Prop({ required: true })
  createdAt: Date;

  @Prop()
  deletedAt?: Date;
}

export const TodoSchema = SchemaFactory.createForClass(Todo);


Enter fullscreen mode Exit fullscreen mode

接下来,我们创建一个 DTO(数据对象模型),用于创建和更新 Todo。首先,我想创建一个基类 DTO



mkdir src/todo/dto

touch src/todo/dto/base-todo.dto.ts


Enter fullscreen mode Exit fullscreen mode

然后我们定义类和属性



// todo/dto/base-todo.dto.ts
export class BaseTodoDto {
   title: string
   description?: string
}


Enter fullscreen mode Exit fullscreen mode

然后,我们为 Create 和 Update 创建一个 DTO,并对其进行扩展,BaseTodoDto这样所有在BaseTodoDto新类下定义的属性都将继承下来,这样我们就不必重写所有这些属性了。从某种意义上说,在这种情况下,我们不需要编写任何样板代码。



touch src/todo/dto/create-todo.dto.ts

touch src/todo/dto/update-todo.dto.ts


Enter fullscreen mode Exit fullscreen mode

然后我们可以定义它



// todo/dto/create-todo.dto.ts
import { BaseTodoDto } from "./base-todo.dto";

export class CreateTodoDto extends BaseTodoDto {}

// todo/dto/update-todo.dto.ts
import { BaseTodoDto } from './base-todo.dto';

export class UpdateTodoDto extends BaseTodoDto {
  completedAt: Date;
}


Enter fullscreen mode Exit fullscreen mode

我们添加了completedAt字段,UpdateTodoDto因此我们允许该字段使用我们指定的特定字段进行更新。

定义模型后,请确保将其导入,TodoModule以便将其识别为Model代码库中的。



import { Module } from '@nestjs/common';
import { TodoService } from './todo.service';
import { TodoController } from './todo.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { Todo, TodoSchema } from './schemas/todo.schema';

@Module({
  providers: [TodoService],
  controllers: [TodoController],
  imports: [
    MongooseModule.forFeature([{ name: Todo.name, schema: TodoSchema }]),
  ],
})
export class TodoModule {}


Enter fullscreen mode Exit fullscreen mode

在 TodoService 中注入模型

在 class 下,我们想定义处理数据的逻辑。因此,在构造函数中,我们将 Model 作为该类的依赖TodoService注入。我指的是刚刚添加到importsTodoModule



import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Todo, TodoDocument } from './schemas/todo.schema';

@Injectable()
export class TodoService {
  constructor(@InjectModel(Todo.name) private readonly model: Model<TodoDocument>) {}
}


Enter fullscreen mode Exit fullscreen mode

在构造函数中,我们使用@InjectModel(Todo.name)注解并传入模型名称,将其设置为私有属性,并赋予其类型为,同时还传入了从 Todo 模型中定义的Model泛型类型为。这将为我们提供 Mongoose 的所有方法,用于查询、修改和创建 MongoDB 数据,这非常方便,因为它提供了自动完成功能。TodoDocumenttodo.schema.ts

你可能还会注意到,它有一个@Injectable()与 Angular 服务类非常相似的注解。这个注解会创建元数据,使得该类能够被服务定位器识别,其他类也可以使用这个类作为依赖项。

定义 CRUD 功能

现在让我们继续定义常用的 CRUD 方法。我们将使用以下方法来编写实现细节:findAll()findOne(id: string)create(createTodoDto: CreateTodoDto)update(id: string, updateTodoDto: UpdateTodoDto)delete(id: string)



import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateTodoDto } from './dto/create-todo.dto';
import { UpdateTodoDto } from './dto/update-todo.dto';
import { Todo, TodoDocument } from './schemas/todo.schema';

@Injectable()
export class TodoService {
constructor(
@InjectModel(Todo.name) private readonly model: Model<TodoDocument>,
) {}

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

async findOne(id: string): Promise<Todo> {
return await this.model.findById(id).exec();
}

async create(createTodoDto: CreateTodoDto): Promise<Todo> {
return await new this.model({
...createTodoDto,
createdAt: new Date(),
}).save();
}

async update(id: string, updateTodoDto: UpdateTodoDto): Promise<Todo> {
return await this.model.findByIdAndUpdate(id, updateTodoDto).exec();
}

async delete(id: string): Promise<Todo> {
return await this.model.findByIdAndDelete(id).exec();
}
}

Enter fullscreen mode Exit fullscreen mode




在 TodoController 中定义方法和路由端点

在我们的 Controller 类中定义路由非常容易,这都要归功于 TypeScript 提供的注解,让一切变得轻而易举!我们必须将 注入TodoService为此类的依赖项Controller,然后使用相应的注解定义所有方法,因为这将决定使用哪种 HTTP 方法来访问功能。

Controller我们将在where中使用以下名称index()来查询所有 Todo、find()查询单个 Todo、create()在 DB 中添加 Todo、update()根据给定的 ID 更新现有 Todo 以及最后delete()删除 Todo。



import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
} from '@nestjs/common';
import { CreateTodoDto } from './dto/create-todo.dto';
import { UpdateTodoDto } from './dto/update-todo.dto';
import { TodoService } from './todo.service';

@Controller('todos')
export class TodoController {
constructor(private readonly service: TodoService) {}

@Get()
async index() {
return await this.service.findAll();
}

@Get(':id')
async find(@Param('id') id: string) {
return await this.service.findOne(id);
}

@Post()
async create(@Body() createTodoDto: CreateTodoDto) {
return await this.service.create(createTodoDto);
}

@Put(':id')
async update(@Param('id') id: string, @Body() updateTodoDto: UpdateTodoDto) {
return await this.service.update(id, updateTodoDto);
}

@Delete(':id')
async delete(@Param('id') id: string) {
return await this.service.delete(id);
}
}

Enter fullscreen mode Exit fullscreen mode




使用 REST 客户端进行测试

您可以使用任何 REST 客户端,但我更喜欢 Insomnia。现在,打开 REST 客户端后,我们就可以继续测试我们创建的 REST API,以便能够添加、更新、删除和读取待办事项。

首先让我们向端点发出 GET 请求todos

图像

它只是返回了一个空数组,这很合理,因为我们没有创建任何待办事项。所以,让我们创建一个吧!

将其作为请求有效负载,然后向同一端点发出 POST 请求,并且它应该作为来自 MongoDB 的新文档返回,其中包含一个_id字段,因为该字段是为我们自动生成的。

图像

您可以创建更多待办事项,但现在我们可以使用相同的端点但使用GET方法再次检查。

图像

现在它以数组的形式返回我们最近创建的待办事项。

现在让我们更新这个待办事项,更改它的标题。首先_id从响应中复制字段。现在,使用这个 ID,让我们创建一个具有相同有效负载的 PUT 请求,但现在我们添加completedAt字段

图像

如您所见,我们已经填写了completedAt字段。您发出的第一个请求返回 200 响应,但响应数据仍然相同,不用担心,因为文档在后台确实已更新。您可以继续通过 GET 请求方法再次检查以查看更改,另一种方法是再次更新文档。因此,将我们现在发出的 PUT 请求次数翻倍,您应该就能看到更改了。

现在我们要删除这个待办事项,那么在本例中,我们使用 DELETE 方法,使用相同的端点,但使用不同的 HTTP 方法。它将返回已删除的文档。

图像

这就是我们现在所知道的全部内容。

结论

如果您想使用 NodeJS 快速创建 REST API,并且您也喜欢 TypeScript,那么 NestJS 就是您的最佳选择!NestJS 不仅适合“快速”实现 REST API,而且由于框架本身鼓励开发人员使用领域驱动设计,因此也非常适合大型项目。

希望你喜欢本教程,如果喜欢的话,记得点赞或投票哦。干杯!

完整的源代码可以从存储库中找到

文章来源:https://dev.to/carlomigueldy/building-a-restful-api-with-nestjs-and-mongodb-mongoose-2165
PREV
10 分钟内建立 P2P 连接 准备 我们的第一个 P2P 连接 它是如何工作的?
NEXT
通过从头构建 UI 框架来学习 JavaScript