应用程序编程接口或API开发是现代软件工程的关键方面,使多样化的软件系统能够无缝进行交互和共享数据。 API是不同软件应用程序之间的合同,概述了它们如何相互通信。
构建Web API的两种最受欢迎的体系结构样式是代表性状态转移(REST)和GraphQl。 REST API使用标准的HTTP方法,例如GET,POST,PUT和DELETE来执行数据操作; GraphQL是一种查询语言,允许客户定义其查询和突变的响应结构,从而提供了更灵活的数据交互方法。
本文旨在研究GraphQl的复杂性,它与REST API的不同以及如何在Nestjs中实现它。
GraphQl与REST API之间的差异
REST(代表性状态转移),由Roy Fielding在2000年推出,是网络应用程序的建筑风格。它使用无状态的客户服务器通信协议,通常是HTTP,服务器提供资源和客户端访问并修改它们。 REST API是从复杂的肥皂协议转移的,启用了Internet系统之间的标准化通信。
另一方面,由Facebook于2012年开发并于2015年公开发行的GraphQL是API的查询语言,旨在解决移动网络中的数据加载效率低下。与REST不同,GraphQL允许客户端准确指定他们需要的数据,以避免过度取得和不足。此功能导致了许多大型公司的采用,并由GraphQl Foundation进行维护。
关键差异
- 端点与单端点:在休息中,您有多个端点,每个端点都有不同的目的。在GraphQL中,您有一个单个端点,可以根据请求中发送的查询或突变处理多个操作。
- 过度提取和不及格:在休息中,您可能最终会过度提取或取得不足的数据,因为服务器定义了每个端点返回的数据。另一方面,在GraphQl中,您确切地指定了查询中想要的数据,这可以帮助避免过度提取和不及格。
- 版本化:在休息中,对API的更改通常会导致新版本。在GraphQL中,您可以通过贬值API字段并添加新的版本来避免版本。
- 错误处理:在休息中,您可以使用HTTP代码指示请求的状态。在GraphQl中,您始终获得200个确定状态,并且在响应主体中处理错误。
- 数据关系:在休息中,链接相关资源可能很复杂。在GraphQl中,您可以使用嵌套查询轻松地在单个请求中获取相关数据。
- API Discovilability :在休息中,您需要参考文档或API源代码,以了解可以获取哪些数据。在GraphQL中,您可以使用graphiql或apollo之类的工具来探索API架构并知道可用于获取的数据。
了解差异
为了真正掌握这两个模型之间的差异,最好使用一个示例。
让我们想象我们有两个用于从设备中检索数据的模型,一种称为Device
,另一个称为City
。在这里,我们有一些示例数据:
设备
{
"id": "1",
"imei": "1234567890",
"address": "123 Main St",
"name": "Device 1",
"city_id": 1,
"type": 1,
"battery": 80,
"lat": 40.7128,
"lon": 74.0060,
"hardware": "hardware_v1",
"firmware": "firmware_v1",
"comments": "This is a test device",
"uptime": 100,
}
城市
{
"id": "1",
"name": "New York",
"country": "USA"
}
使用 REST API 查询
在REST API中,您可能有一个端点可以获取设备详细信息:
GET /devices/:id
这可能会返回:
{
"id": "1",
"imei": "1234567890",
"address": "123 Main St",
"name": "Device 1",
"city_id": 1,
"type": 1,
"battery": 80,
"lat": 40.7128,
"lon": 74.0060,
"hardware": "hardware_v1",
"firmware": "firmware_v1",
"comments": "This is a test device"
}
如果您只需要 name
, battery_main
和 battery_lock
,则您的数据将过度取回数据,因为端点返回
如果您还需要了解设备所在的 city
的详细信息,则必须向 City
端点提出另一个请求city_id
:
GET /cities/:id
这是 fetting下的的一个示例,因为初始请求没有提供您需要的所有数据。
使用GraphQl查询
在GraphQl API中,您可以提出一个请求以获取所需的确切数据:
query {
device(id: "1") {
name
battery
city {
name
country
}
}
}
这可能会返回:
{
"data": {
"device": {
"name": "Device 1",
"battery": 80,
"city": {
"name": "New York",
"country": "USA"
}
}
}
}
在这种情况下,使用单个GraphQl查询,您可以准确获取所需的数据:设备的名称,主电池级别,锁定电池级别和城市详细信息。没有过度提取,没有不足的挑战。
使用Nestjs 实现GraphQl API
NestJS是一种渐进式node.js框架,是构建有效,可扩展和可维护应用程序的绝佳选择。它是由打字稿构建的,提供了JavaScript和TypeScript功能的最佳。
Nestjs支持的高级用法之一是创建GraphQl API。本指南将引导您浏览使用Nestjs和Apollo实现GraphQl API的步骤。
设置Nestjs
从安装Nestjs开始。您可以使用Nest CLI,不仅可以建立一个新项目,还可以帮助生成应用程序组件。您可以使用NPM在全球安装它:
npm i -g @nestjs/cli
创建一个新项目:
nest new my-graphql-project
导航到您的新项目:
cd my-graphql-project
安装GraphQl和Apollo
为了实现我们的GraphQl API,我们决定使用Apollo服务器。这是一种受欢迎且广泛使用的开源解决方案,是社区驱动且维护良好的。
通过选择Apollo服务器来实现我们的GraphQl API,我们可以从其许多功能中受益,包括其灵活性,可扩展性和易用性。此外,阿波罗社区提供了大量的资源和支持,这可以帮助我们快速有效地实施我们的API。考虑到所有这些因素,我们可以相信阿波罗服务器是我们需求的理想选择。
让我们从安装所有必要的依赖项开始:
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
现在我们需要将Apollo导入App
模块,您可以通过选择某些关键选项来配置。
在我们的情况下,我们将使用autoSchemaFile: true
bbled,因为我们使用 code-first 方法来定义我们的API(我们使用装饰符和打字稿类来生成相应的GraphQL架构)。我们还禁用默认的GraphQl操场以使用Apollo服务器,该操作服务器提供了更多的incionality,并且具有更易于用户友好的UI。
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
playground: false,
plugins: [ApolloServerPluginLandingPageLocalDefault()],
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
创建GraphQl模块
在Nestjs中,模块提供了组织代码的好方法。您可以使用以下代码创建GraphQl模块:
nest generate module devices
此命令创建一个新的模块,并在 app.module.ts
中注册它。
定义GraphQl模式
在GraphQl中,架构是您的API的核心。它定义了数据的形状和可用的操作。在Nestjs中,您使用打字稿类和装饰器定义模式。
您可以在 device/entities/device.entity.ts
文件中创建 device
架构,并具有以下代码:
import { ObjectType, Field, Int } from '@nestjs/graphql';
@ObjectType()
export class Device {
@Field(() => String, { description: 'Device ID' })
id: string;
@Field(() => String, { nullable: true })
imei: string;
@Field(() => String, { nullable: true })
address: string;
@Field(() => String, { nullable: true })
name: string;
@Field(type => Int, { nullable: true })
city_id: number;
@Field(() => Number, { nullable: true })
type: number;
@Field(() => Number, { nullable: true })
battery: number;
@Field(() => Number, { nullable: true })
lat: number;
@Field(() => Number, { nullable: true })
lon: number;
@Field(() => String, { nullable: true })
hardware: string;
@Field(() => String, { nullable: true })
firmware: string;
@Field(() => String, { nullable: true })
comments: string;
}
创建解析器和服务
解析器等效于控制器。他们处理传入的查询和突变,并执行所需的操作。在devices/devices.resolver.ts,
中创建 devices
解析器使用以下代码。
nest generate resolver devices
在 devices.resolver.ts
文件中,定义查询以获取设备:
import { Query, Resolver } from '@nestjs/graphql';
import { Device } from './entities/device.entity';
import { DevicesService } from './devices.service';
@Resolver()
export class DevicesResolver {
constructor(private readonly devicesService: DevicesService) { }
@Query(() => [Device], { name: 'devices' })
findAll() {
return this.devicesService.findAll();
}
}
您可以看到,解析器通过调用DevicesService
的函数来结束,因为我们使用服务来抽象数据操作层。现在写这个文件。
nest generate service devices
在 devices.service.ts
文件中,您可以定义该功能以从数据存储库中获取设备。在我们的示例中,我们返回一个硬编码的设备,但是最常见的用例将使用TypeOMM查询数据库。
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevicesService {
findAll() {
return new Array({
id: '1',
imei: '123456789012345',
address: '123 Main St',
name: 'My Device',
city_id: 1,
type: 1,
battery: 100,
lat: 0,
lon: 0,
hardware: '1.0',
firmware: '1.0',
comments: 'This is a comment'
});
}
}
测试您的API
编写以下内容以启动您的服务器:
npm run start
在浏览器中导航到 http://localhost:3000/graphql
。您会看到Apollo GraphQl Explorer和Playground,您可以在其中列出所有查询和突变并测试您的API。如您所见,只有一个GraphQl端点,而不是RESTAPI中的多个端点。
在左侧,您将看到API中定义的所有查询。中间部分显示您要尝试的当前查询,右侧部分显示了服务器的响应。您可以在UI非常直观的情况下玩耍。
在这里,您有一个查询,可获取设备的id
,name
和 address
,并显示服务器如何使用数据响应。
在这里,您可以清楚地看到GraphQL与REST API的灵活性。您可以定义可以查询哪些字段,而不是在REST API上获取固定数量的字段;您甚至可以在查询中添加关系。
GraphQl突变
定义GraphQl模式并测试我们的第一个查询后的下一步是实现突变,允许您在服务器上创建,更新或删除数据的功能。
在我们的设备API的情况下,我们可能会有突变使我们可以创建,更新或删除设备。让我们创建一个示例突变,其中涉及创建具有特定属性集的新设备。
首先,我们创建和Input
定义创建设备所需的数据的实体(我们创建一个简单的突变,使我们只能使用imei
字段创建设备,并且可选为name
)。因此,让我们创建文件devices/dto/create-device.input.ts
:
import { InputType, Int, Field } from '@nestjs/graphql';
@InputType()
export class CreateDeviceInput {
@Field(() => String, { description: 'Device IMEI', nullable: false })
imei: String
@Field(() => String, { description: 'Device Name', nullable: true })
name: String!
}
现在我们在我们的devices.resolver.ts
中添加突变操作:
import { Query, Resolver } from '@nestjs/graphql';
import { Device } from './entities/device.entity';
import { DevicesService } from './devices.service';
@Resolver()
export class DevicesResolver {
constructor(private readonly devicesService: DevicesService) { }
@Query(() => [Device], { name: 'devices' })
findAll() {
return this.devicesService.findAll();
}
@Mutation(() => Device)
createDevice(@Args('createDeviceInput') createDeviceInput: CreateDeviceInput) {
return this.devicesService.create(createDeviceInput);
}
}
最后,您可以修改 devices.service.ts
文件以添加新功能,并将新数据库添加到当前基础数据存储服务中。但是,此功能不超出本文的范围,因为我们专注于理解GraphQl的工作方式。
结论
使用Nestjs实现GraphQL API,为现代API开发提供了强大,有效的解决方案。 Nestjs凭借其模块化结构和使用打字稿,为构建可扩展应用程序提供了坚实的基础。
使用GraphQL API可以帮助消除与REST API相关的常见问题,例如过度取决和不及格,并删除版本化的需求。 GraphQL中的单端点简化了客户端服务器的交互,并且在单个请求中获取相关数据的功能使其成为处理复杂数据关系的强大工具。
Nestjs和GraphQL的组合为开发人员提供了一个强大的工具包,可建立有效,可扩展和可维护的API。无论您是构建小型应用程序还是大型系统,这种组合都可以帮助您有效,有效地实现开发目标。
在随后的文章中,我们将学习如何在不同的前端开发平台上查询GraphQl。所以请继续关注。