使用 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 有更好的了解。但是,如果您不熟悉这些要求,我将列出我推荐您观看的内容以供学习:
- Fireship的 TypeScript
- Fireship的 NodeJS
- Traversy Media的 ExpressJS
我也推荐你订阅这些YouTube频道,因为它们内容质量很高,而且免费!我还有其他喜欢的YouTube频道,但我会在另一篇文章里分享。
如果你是一名前端开发人员,并且已经使用 Angular 一段时间了,那么你应该对 NestJS 非常熟悉,因为 NestJS 的代码结构与 Angular 非常相似!它支持依赖注入、模块、使用 CLI 生成代码等等!
安装
本安装指南将基于 Linux,因为我在 Windows 上使用 WSL2,这是我的偏好,并且觉得它更方便。我相信安装过程非常相似,但如果是其他平台,我建议您参考此处的文档。
安装 Nest CLI
打开终端并执行此命令来安装 Nest CLI
sudo npm install -g @nestjs/cli
要测试它是否已成功安装,只需执行以下命令,它将告诉您当前为 Nest CLI 安装的版本
nest -v
创建新的 Nest 项目
导航到您的项目目录或您喜欢的任何目录,然后运行以下命令来安装新项目
nest new todo-rest-app
如果它询问您选择哪个包管理器,只需选择您喜欢的任何一个,但在本文中我将选择 NPM。
现在等待整个 CLI 为您搭建新的启动项目。
在 IDE 中打开项目
安装完成后,用你喜欢的代码编辑器打开它。我这里用的是 VSCode(Visual Studio Code),所以我会在终端里执行这个命令
cd todo-rest-app && code .
然后就会打开你的 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
这应该创建一个名为“todo”的文件夹,它还将在数组TodoService
下和数组中添加。providers
TodoModule
TodoController
controllers
创建 Todo 模型/模式
在编写处理数据并将其暴露给 REST API 的代码之前,我们首先要为 Todo 创建一个数据模型。因此,让我们使用 Mongoose 包创建一个模式,然后安装它。
npm install --save @nestjs/mongoose mongoose
安装完成后,请确保将其添加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 {}
MongooseModule.forRoot()
添加后,AppModule
我们就可以继续定义我们的 Todo 模式了,因此请转到“todo”目录,因为此功能目录已由 CLI 生成,因此在此目录下创建一个名为“schemas”的文件夹,它是 Todo 模式所在的位置
或者您可以使用这个终端命令来执行此操作
mkdir src/todo/schemas && touch src/todo/schemas/todo.schema.ts
然后让我们定义我们的 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);
接下来,我们创建一个 DTO(数据对象模型),用于创建和更新 Todo。首先,我想创建一个基类 DTO
mkdir src/todo/dto
touch src/todo/dto/base-todo.dto.ts
然后我们定义类和属性
// todo/dto/base-todo.dto.ts
export class BaseTodoDto {
title: string
description?: string
}
然后,我们为 Create 和 Update 创建一个 DTO,并对其进行扩展,BaseTodoDto
这样所有在BaseTodoDto
新类下定义的属性都将继承下来,这样我们就不必重写所有这些属性了。从某种意义上说,在这种情况下,我们不需要编写任何样板代码。
touch src/todo/dto/create-todo.dto.ts
touch src/todo/dto/update-todo.dto.ts
然后我们可以定义它
// 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;
}
我们添加了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 {}
在 TodoService 中注入模型
在 class 下,我们想定义处理数据的逻辑。因此,在构造函数中,我们将 Model 作为该类的依赖项TodoService
注入。我指的是刚刚添加到imports
TodoModule
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>) {}
}
在构造函数中,我们使用@InjectModel(Todo.name)
注解并传入模型名称,将其设置为私有属性,并赋予其类型为,同时还传入了从 Todo 模型中定义的Model
泛型类型为。这将为我们提供 Mongoose 的所有方法,用于查询、修改和创建 MongoDB 数据,这非常方便,因为它提供了自动完成功能。TodoDocument
todo.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();
}
}
在 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);
}
}
使用 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