使用Nestjs和Typeorm构建您的第一个REST API并与Postman进行测试
#node #nestjs #backend #restapi

REST API

REST API是一种使用HTTP请求的Web API,允许客户使用标准HTTP方法访问和操纵URIS确定的资源。它旨在无状态,通常用于将数据和功能从服务器端应用程序暴露到客户端应用程序或第三方开发人员。

不是

Nestjs是一个节点.js框架,用于构建可扩展和高效的服务器端应用程序,其内置功能和模块用于依赖项注入,中间件,路由等,并支持多个数据库和测试工具。<<<<<<<<<<<<<<<<<<< /p>

typeorm

typeorm是用于打字稿和JavaScript的对象凝聚映射(ORM)库,它提供了一个简化的接口,用于使用面向对象的编程技术使用关系数据库。它允许开发人员使用打字稿类和装饰器定义数据库模式,支持多个数据库,并提供诸如数据库迁移之类的功能。

让我们开始

首先,您必须安装Nestjs CLI全球

npm i -g @nestjs/cli

然后创建一个新项目

nest new rest-api 

在创建项目之前,Nestjs将询问您使用哪个包装管理器,但是您可以选择NPM,但您可以选择NPM,YARN或PNPM。

所以让我们看看Nestjs为我们生成了什么。

Image description

  • node_modules :我们依赖关系的文件夹 测试:Nestjs建议您进行端到端测试。
  • 样板文件:用于软件包管理,打字稿配置和静态代码检查。
  • src :一个包含多个核心文件的文件夹,我们将在其中编写代码。

安装完成后,您可以运行以下命令运行您的应用程序。

npm run start 

此命令将在main.ts文件中指示的端口上运行您的应用程序。您也可以运行

npm run start:dev

此命令每次编辑文件时都会运行您的应用程序并重新加载。

现在创建了我们的应用程序,我们知道如何运行它,让我们构建。我们想要一个可以处理用户和基本CRUD操作的应用程序。这些用户应存储在数据库中。

数据库设置

我们将使用Typeorm和MySQL进行存储。 Nestjs与@nestjs/typeorm软件包的Typeorm开箱即用。我们只需要安装软件包。

npm install --save @nestjs/typeorm typeorm mysql2

,然后使用@nestjs/typeormalongside使用TypeOrmModuleprovide连接数据库。2

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: '',
      database: 'restapi',
      entities: ['src/**/**.entity{.ts,.js}'],
      synchronize: true,
    }),
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

现在我们的数据库已连接到项目。

资源处理

Nestjs管理资源,资源是代表您应用程序域的一部分的文件夹或模块。对于应用程序的每个资源,您都有一个控制器,服务,DTO和实体。在我们的情况下,我们想管理用户,因此用户是我们的资源。

要创建一个新资源,我们通过添加我们正在创建的资源的名称来点击下面的命令。

nest g resource users

CLI会问您两个问题:

  • 您使用哪个运输层?:选择REST API
  • 您想生成Crud入口点吗?:是

然后将创建以下文件夹和文件。

实体

现在,我们已经设置了所有样板,我们需要定义用户实体,为此,我们将编辑用户。

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column('text')
  firstname: string;

  @Column('text')
  lastname: string;

  @Column('text')
  email: string;
}

首先,我们需要在类定义之前添加@entity Decorator,以告诉Typeorm下一个类是一个实体,然后对于属性,您有两个装饰器,@PrimaryGeneratedColumn以创建一个主和自动启动密钥,并且@Column创建其他表列。要定义列类型,我们只需要在@Columndecorator中定义它,在我们的情况下,这三个属性是文本,但是您可以使用数字,布尔值等。

DTO

当用户的资源为我们生成时,有一个名为DTO的文件夹,带有两个文件create-user.dto.ts和update-user.dto.ts

dto是一类,可以在为数据库创建新的条目时塑造我们想要接收的数据。 create-user.dto.ts将包含我们要收到的属性以创建新用户,如果这些属性可用,请求将无法到达服务层。在这种情况下,我们只有三个要收到的属性,所以让我们更新create-user.dto.ts

export class CreateUserDto {
  public firstname: string;
  public lastname: string;
  public email: string;
}

服务

服务层是我们实现业务逻辑的层,它也与与数据库交互的图层进行通信,主要称为存储库层和控制器层。

通常,服务层接收用户从控制器发送的数据,该服务层在这些数据上应用业务逻辑,然后调用存储库层将其存储在数据库中。

当您通过CLI生成新资源时,您将拥有一个使用几种方法创建的服务。

import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  create(createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }

  findAll() {
    return `This action returns all users`;
  }

  findOne(id: number) {
    return `This action returns a #${id} user`;
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  remove(id: number) {
    return `This action removes a #${id} user`;
  }
}

我们将编辑此课程。在上面的简介中,我们说服务层需要调用存储库层与数据库进行交互。

由于我们使用的是TypeOm,它提供了一个内置类,该类带有处理与给定实体相关的数据的方法。

Nestjs偏爱依赖注入允许两个不同的类相互作用,依赖注入是对控制(IOC)技术的反转,其中您将依赖关系的实例化委托给IOC容器,而不是在您自己的代码中执行它。让我们编辑用户服务。

constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

要向存储库注入我们必须在构造函数中,使用注入式装饰器在构造函数中执行此操作,并声明一个私有变量,该变量的类型存储库由typeorm提供的类型存储库与我们要管理的实体提供。

但是,每当我们使用外部依赖性时,我们都需要导入它,因此我们要使用它的服务中可用。要导入外部依赖性,我们必须编辑我们正在使用的资源的module

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm/dist';
import { User } from './entities/user.entity';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

完成后,该类别的每个功能都可以使用UserRepository变量。

现在设置了存储库,我们可以使用它来读取,创建,更新和删除用户。

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {}

  async create(createUserDto: CreateUserDto) {
    return await this.userRepository.save(createUserDto);
  }

  async findAll() {
    return await this.userRepository.find();
  }

  async findOne(id: number) {
    return await this.userRepository.findOne({ where: { id } });
  }

  async update(id: number, updateUserDto: UpdateUserDto) {
    const toUpdate = await this.userRepository.findOne({ where: { id } });

    const updated = Object.assign(toUpdate, updateUserDto);

    return await this.userRepository.save(updated);
  }

  async remove(id: number) {
    return await this.userRepository.delete(id);
  }
}

我们已经将所有方法转换为异步方法,因为存储库的方法返回承诺,我们必须处理这些承诺。

现在,我们已经设置了服务,注入存储库和服务中的每种方法都与存储库通信以存储其接收到的数据,我们可以移至下一层。

控制器

控制器层是我们处理用户请求并将数据发送到服务层的层。控制器中的每种方法都将转换为最终用户交互的终点。

我们已经使用CLI生成用户的资源,因此我们已经准备好使用一个控制器,而无需任何更改。让我看看它的外观:

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

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

由于控制器必须与服务层进行交互,因此我们遵循与服务层相同的方法,我们使用依赖项注入来注入服务类。

在每个方法定义将用于调用端点的方法之前的每个装饰器。

更新和创建方法中的身体装饰器用于验证请求主体。您可以提供一种类型,在这里,我们提供我们在文章第一部分中创建的DTO类,以确保我们收到的数据按照我们的需求进行了塑造。

Decorator参数用于通过定义参数的名称使用其类型来获取请求的参数。

由于定义了端点方法,并且验证了请求参数和主体,因此我们调用将应用我们的业务逻辑的服务方法,然后致电存储库与数据库进行交互。

测试API

我们将使用postman测试我们的API。

Postman是用于构建,测试和记录API的工具。它简化了提出HTTP请求和检查响应的过程,可以轻松在团队成员之间进行协作,并支持与CI/CD管道的自动测试和集成。

获取用户列表

要获取用户列表,我们必须设置端点URL和GET方法。

Image description

创建新用户

要创建一个新用户,我们使用相同的URL,但是我们更改了发布的方法,我们还需要在请求的标题中添加一个内容类型属性,以表明请求正文中发送的数据是以JSON格式。

Image description
建立内容类型属性后。我们可以编辑请求主体以发送我们的数据。

Image description

请求主体是我们设置用户数据的地方,您可以看到我们将收到201个状态,这意味着用户已成功创建,我们还收到了用他的ID的用户数据。

删除用户

要删除用户,我们必须将其ID添加到URL中并更改删除的方法。

Image description

编辑用户

要编辑用户,我们必须将其ID添加到URL中,更改修补方法,并设置要在请求正文中编辑的值。

Image description

正如我们在上图中看到的那样,用户的电子邮件已更改。

结论

总而言之,使用Nestjs,TypeOm和MySQL构建REST API可以是创建可靠且可扩展的Web应用程序的强大组合。使用Nestjs,您可以利用其模块化体系结构和依赖注入来构建高度可测试和可维护的代码。 Typeorm通过提供了一组丰富的工具和功能来处理数据访问和操作,从而简化了使用数据库的过程。 Postman提供了一个用户友好的接口来测试您的REST API。

我希望本文有助于指导您通过这些技术构建REST API的过程。