从0开始构建 “完美” 的后端:使用 Nest.js 构建 RESTful API
代码是从项目中截取,属于伪代码,并不保证一定可以正确运行,但是如果您愿意折腾一下,我也认为没有问题,花点时间,运行起来也是可以的
废话不说,直接上手!
今天我们将使用NestJS创建一个简单的任务管理应用程序。该应用程序将具有以下功能:
- 用户可以创建、读取、更新和删除任务。
- 所有任务都存储在MySQL数据库中。
接下来,我们将分步骤创建这个应用程序。
步骤1:创建NestJS应用程序
首先,我们需要安装NestJS CLI,使用以下命令安装:
npm install -g @nestjs/cli
然后,我们可以使用以下命令创建一个新的NestJS应用程序:
nest new task-manager
步骤2:安装必要的依赖项
在这一步,我们将安装一些必要的依赖项。打开项目文件夹并使用以下命令安装依赖项:
cd task-manager
npm install --save @nestjs/typeorm typeorm mysql2
在这里,我们安装了@nestjs/typeorm
和typeorm
来使用TypeORM库来连接MySQL数据库。我们还安装了mysql2
作为MySQL驱动程序。
步骤3:创建数据库
在这一步,我们将创建MySQL数据库和表。我们可以使用MySQL命令行工具或任何MySQL客户端来创建数据库和表。在这里,我们将使用以下命令在MySQL中创建一个名为task_manager
的数据库:
CREATE DATABASE task_manager;
接下来,我们将创建一个名为task
的表,该表将存储任务的信息。以下是创建任务表的SQL查询:
CREATE TABLE task (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
status VARCHAR(50) NOT NULL DEFAULT 'OPEN'
);
步骤4:配置TypeORM
在这一步,我们将配置TypeORM来连接MySQL数据库并与任务实体进行交互。打开src/app.module.ts
文件并使用以下代码更新它:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TaskModule } from './task/task.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'password',
database: 'task_manager',
autoLoadEntities: true,
synchronize: true,
}),
TaskModule,
],
})
export class AppModule {}
在这里,我们使用TypeOrmModule.forRoot()
方法来配置TypeORM。我们指定了MySQL数据库的连接参数,如主机名、端口、用户名、密码和数据库名称。我们还启用了实体自动加载和数据库同步。
步骤5:创建任务实体
在这一步,我们将创建一个任务实体,用于表示任务的数据模型。打开src/task/task.entity.ts
文件并使用以下代码更新它:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Task {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column({ nullable: true })
description?: string;
@Column({ default: 'OPEN' })
status: string;
}
在这里,我们使用TypeORM的装饰器来定义实体和其属性。@Entity()
装饰器用于将类标记为实体。@PrimaryGeneratedColumn()
装饰器用于指定自动生成的主键。@Column()
装饰器用于定义列。我们还使用了一些选项,如nullable
和default
,以指定列的特定属性。
步骤6:创建任务模块
在这一步,我们将创建一个任务模块。模块是NestJS应用程序的核心组件之一,用于将代码组织成相关性质的代码块。打开src/task/task.module.ts
文件并使用以下代码更新它:
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Task } from './task.entity';
import { TaskController } from './task.controller';
import { TaskService } from './task.service';
@Module({
imports: [TypeOrmModule.forFeature([Task])],
controllers: [TaskController],
providers: [TaskService],
})
export class TaskModule {}
在这里,我们使用@Module()
装饰器来标记该类为模块。我们使用TypeOrmModule.forFeature()
方法来导入任务实体。我们还将任务控制器和任务服务提供程序添加到该模块的控制器和提供程序列表中。
步骤7:创建任务控制器
在这一步,我们将创建一个任务控制器,用于处理来自客户端的HTTP请求。打开src/task/task.controller.ts
文件并使用以下代码更新它:
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
import { TaskService } from './task.service';
import { Task } from './task.entity';
@Controller('tasks')
export class TaskController {
constructor(private readonly taskService: TaskService) {}
@Get()
findAll(): Promise<Task[]> {
return this.taskService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string): Promise<Task> {
return this.taskService.findOne(parseInt(id));
}
@Post()
create(@Body() task: Task): Promise<Task> {
return this.taskService.create(task);
}
@Put(':id')
update(@Param('id') id: string, @Body() task: Task): Promise<Task> {
return this.taskService.update(parseInt(id), task);
}
@Delete(':id')
delete(@Param('id') id: string): Promise<void> {
return this.taskService.delete(parseInt(id));
}
}
在这里,我们使用@Controller()
装饰器将该类标记为控制器,并指定其前缀为/tasks
。我们还注入了任务服务,并使用@Get()
、@Post()
、@Put()
、@Delete()
装饰器来定义处理HTTP GET、POST、PUT和DELETE请求的方法。我们还使用@Body()
和@Param()
装饰器来访问请求体和URL参数。
步骤8:创建任务服务
在这一步,我们将创建一个任务服务,用于处理应用程序逻辑和数据访问。打开src/task/task.service.ts
文件并使用以下代码更新它:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Task } from './task.entity';
@Injectable()
export class TaskService {
constructor(
@InjectRepository(Task)
private readonly taskRepository: Repository<Task>,
) {}
async findAll(): Promise<Task[]> {
return this.taskRepository.find();
}
async findOne(id: number): Promise<Task> {
return this.taskRepository.findOne(id);
}
async create(task: Task): Promise<Task> {
return this.taskRepository.save(task);
}
async update(id: number, task: Task): Promise<Task> {
const updatedTask = await this.taskRepository.findOne(id);
if (!updatedTask) {
return null;
}
return this.taskRepository.save({ ...updatedTask, ...task });
}
async delete(id: number): Promise<void> {
await this.taskRepository.delete(id);
}
}
在这里,我们使用@Injectable()
装饰器将该类标记为服务。我们使用@InjectRepository()
装饰器来注入任务存储库,并使用Repository
泛型来定义存储库的类型。我们定义了一些方法来处理任务数据的CRUD操作,这些方法使用存储库中的实际方法来执行这些操作。
步骤9:启动应用程序
现在我们已经创建了所有必要的组件来构建我们的应用程序,现在让我们启动应用程序并测试它是否正常工作。在终端中使用以下命令启动应用程序:
npm run start:dev
如果一切正常,您应该会看到一条消息,指示应用程序正在监听端口3000。现在让我们使用curl测试我们的应用程序。
使用以下命令测试GET /tasks
路由:
curl http://localhost:3000/tasks
您应该会看到一个空的JSON数组。这是因为我们还没有创建任何任务。现在让我们使用以下命令测试POST /tasks
路由,创建一些任务:
curl -H "Content-Type: application/json" -X POST -d '{"title":"Task 1", "description":"Description 1", "status":"OPEN"}' http://localhost:3000/tasks
curl -H "Content-Type: application/json" -X POST -d '{"title":"Task 2", "description":"Description 2", "status":"OPEN"}' http://localhost:3000/tasks
curl -H "Content-Type: application/json" -X POST -d '{"title":"Task 3", "description":"Description 3", "status":"OPEN"}' http://localhost:3000/tasks
现在使用以下命令测试GET /tasks
路由,查看我们创建的任务:
curl http://localhost:3000/tasks
您应该会看到一个包含三个任务的JSON数组。接下来,让我们测试GET /tasks/:id
路由,使用任务的ID检索单个任务:
curl http://localhost:3000/tasks/1
您应该会看到一个JSON响应,包含ID为1的任务的详细信息。现在让我们测试PUT /tasks/:id
路由,使用任务的ID更新任务:
curl -H "Content-Type: application/json" -X PUT -d '{"status":"IN_PROGRESS"}' http://localhost:3000/tasks/1
现在使用GET /tasks/1
路由检索任务,您应该会看到任务的状态已更新为“IN_PROGRESS”。最后,让我们测试DELETE /tasks/:id
路由,使用任务的ID删除任务:
curl -X DELETE http://localhost:3000/tasks/1
现在再次使用GET /tasks
路由检索任务,您应该只看到两个任务,因为我们已经删除了ID为1的任务。
常用的装饰器
在 Nest.js 中,装饰器是一种特殊类型的 TypeScript 注释,可以用来附加元数据到类、方法、属性和参数上。这些元数据可以在运行时被检索,从而让 Nest.js 能够对应用程序的行为进行更多的自动化。
以下是一些常用的 Nest.js 装饰器:
@Controller()
@Controller()
装饰器用于定义一个控制器类。控制器类负责处理 HTTP 请求,通常会包含一组路由处理程序,用于处理请求和生成响应。
@Controller('users')
export class UsersController {
// ...
}
在这个例子中,@Controller('users')
装饰器指定了控制器的根路径为 /users
。
@Get()
@Get()
装饰器用于定义一个 HTTP GET 请求的路由处理程序。
@Controller('users')
export class UsersController {
@Get()
findAll(): string {
return '此操作返回所有用户';
}
}
在这个例子中,@Get()
装饰器指定了这个路由处理程序处理 HTTP GET 请求。
@Post()
@Post()
装饰器用于定义一个 HTTP POST 请求的路由处理程序。
@Controller('users')
export class UsersController {
@Post()
create(): string {
return '此操作将创建一个新用户';
}
}
在这个例子中,@Post()
装饰器指定了这个路由处理程序处理 HTTP POST 请求。
@Param()
@Param()
装饰器用于注入 URL 参数的值到路由处理程序的方法参数中。
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string): string {
return `此操作返回id为${id}的用户 `;
}
}
在这个例子中,@Param('id')
装饰器指定了将 URL 参数 id
的值注入到方法参数 id
中。
@Body()
@Body()
装饰器用于注入请求体中的数据到路由处理程序的方法参数中。
@Controller('users')
export class UsersController {
@Post()
create(@Body() user: CreateUserDto): string {
return `此操作创建一个名为${user.name}的新用户 `;
}
}
在这个例子中,@Body()
装饰器指定了将请求体中的数据注入到方法参数 user
中,并使用 CreateUserDto
类型进行验证和转换。
@Query()
@Query()
装饰器用于注入查询参数的值到路由处理程序的方法参数中。
@Controller('users')
export class UsersController {
@Get()
findAll(@Query() query: GetUsersQueryDto): string {
return `此操作返回所有具有状态 ${query.status} 的用户`;
}
}
在这个例子中,@Query()
装饰器指定了将查询参数注入到方法参数 query
中,并使用 GetUsersQueryDto
类型进行验证和转换。
@UseGuards()
@UseGuards()
装饰器用于将一个或多个守卫绑定到路由处理程序或控制器上。
@Controller('users')
@UseGuards(AuthGuard)
export class UsersController {
// ...
}
在这个例子中,@UseGuards(AuthGuard)
装饰器将 AuthGuard
守卫绑定到 UsersController
控制器上。
@UseInterceptors()
@UseInterceptors()
装饰器用于将一个或多个拦截器绑定到路由处理程序或控制器上。
@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController {
// ...
}
在这个例子中,@UseInterceptors(LoggingInterceptor)
装饰器将 LoggingInterceptor
拦截器绑定到 UsersController
控制器上。
这些装饰器只是 Nest.js 中可用的一小部分。通过使用它们,您可以更好地组织和管理应用程序的逻辑,以及提高应用程序的可维护性和可测试性。
总结
在本文中,我们已经学习了如何使用Nest.js框架创建一个简单的REST API。我们学习了如何使用Nest CLI创建一个新的Nest.js应用程序,如何定义路由和控制器,如何创建实体和服务,以及如何使用TypeORM和MySQL进行数据持久化。我们还了解了Nest.js中常用的装饰器,并使用它们来访问请求体和URL参数。
转载自:https://juejin.cn/post/7231860080470147128