GraphQL 与 REST API
介绍
GraphQL 基本介绍
正文
REST API
REST API 只是一个遵循已定义的某些规则和体系结构的 API,主要用于促进不同 Web 服务之间的通信。REST API 通过 HTTP 协议请求信息,并返回一个响应,其中包含可用于在应用程序中提供的信息。
例如,如果你正在构建一个每天推荐新发行专辑的音乐应用,则需要编译一个新版本列表以供选择,然后在应用中编译一些功能来显示这些专辑。
您可以创建一个数据库并添加您想要包含的所有专辑,并在每次新版本发布时手动更新,或者您可以使用向Spotify的REST API发出请求,
该API访问Spotify的播放列表数据库并说'嘿',请给我一个刚刚在Spotify上发布的项目。
REST API 体系结构通过提供如下所示的终结点(endpoints)列表,使服务(如音乐应用)可请求信息:
https://api.spotify.com/v1/browse/new-releases
终结点(endpoints)从基本 URL(在本例中为 api.spotify.com)开始,然后从那里分支。
构建API的Spotify工程师坐下来思考将他们希望人们访问的所有信息分解到不同端点的最佳方法。
有一个用于访问新版本的端点,一个用于将用户添加为特定播放列表的关注者的端点,一个用于访问有关播客剧集信息的端点,您可以在此处查看所有可用的端点。
有一堆端点是由Spotify工程师定义的,以最好地满足他们的需求。
解释:(我们日常开发会根据需要在同一个业务上定义非常多的查询接口来满足业务需要)
REST的问题
因为这些端点是由其他工程师设计的,以最适合他们的需求,这很可能意味着它们不是最适合您的需求。您被迫使用预定义的端点,并且可能不知道给定请求将返回何种类型的响应。
https://api.spotify.com/v1/browse/new-releases
仅通过查看这个端点,我们不知道从get请求返回什么样的响应。它是否告诉我们它发布的月份,以便我们可以按月分类?它能告诉我们它的音乐类型吗?它能告诉我们到目前为止它被播放了多少次吗?我们不知道,作为一名开发人员,您希望确切地知道您可以期待什么样的数据。
解释:(有些时候我们会将就的去使用一些已经写好的接口,但是这个接口索要的信息和回执不一定完全符合业务需要)
字段冗余
如果我们通过点击这个端点API.spotify.com/v1/playlists/{playlist_id}请求访问播放列表API,我们将得到以下响应:
{
"albums" : {
"href" : "https://api.spotify.com/v1/browse/new-releases?country=SE&offset=0&limit=20",
"items" : [ {
"album_type" : "single",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2RdwBSPQiwcmiDo9kixcl8"
},
"href" : "https://api.spotify.com/v1/artists/2RdwBSPQiwcmiDo9kixcl8",
"id" : "2RdwBSPQiwcmiDo9kixcl8",
"name" : "Pharrell Williams",
"type" : "artist",
"uri" : "spotify:artist:2RdwBSPQiwcmiDo9kixcl8"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "ID", "IE", "IS", "IT", "JP", "LI", "LT", "LU", "LV", "MC", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "SE", "SG", "SK", "SV", "TR", "TW", "US", "UY" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/5ZX4m5aVSmWQ5iHAPQpT71"
},
"href" : "https://api.spotify.com/v1/albums/5ZX4m5aVSmWQ5iHAPQpT71",
"id" : "5ZX4m5aVSmWQ5iHAPQpT71",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/e6b635ebe3ef4ba22492f5698a7b5d417f78b88a",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/92ae5b0fe64870c09004dd2e745a4fb1bf7de39d",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/8a7ab6fc2c9f678308ba0f694ecd5718dc6bc930",
"width" : 64
} ],
"name" : "Runnin'",
"type" : "album",
"uri" : "spotify:album:5ZX4m5aVSmWQ5iHAPQpT71"
}, {
"album_type" : "single",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4"
},
"href" : "https://api.spotify.com/v1/artists/3TVXtAsR1Inumwj472S9r4",
"id" : "3TVXtAsR1Inumwj472S9r4",
"name" : "Drake",
"type" : "artist",
"uri" : "spotify:artist:3TVXtAsR1Inumwj472S9r4"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "ID", "IE", "IS", "IT", "JP", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "SE", "SG", "SK", "SV", "TR", "TW", "UY" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/0geTzdk2InlqIoB16fW9Nd"
},
"href" : "https://api.spotify.com/v1/albums/0geTzdk2InlqIoB16fW9Nd",
"id" : "0geTzdk2InlqIoB16fW9Nd",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/d40e9c3d22bde2fbdb2ecc03cccd7a0e77f42e4c",
"width" : 640
}
..........
}
}
你会用一堆与你的应用程序无关的信息回复这个超长响应。你所需要的只是项目名称、艺术家姓名以及要在应用程序中显示的封面艺术。这个问题被称为过度蚀刻。在我们正在描述的模拟音乐应用程序这样的小型应用程序中,这并不是一个大到足以在应用程序中造成任何明显的延迟的问题,但它很烦人。现在,您必须使用这个JSON响应来过滤所需的内容。
解释:(冗余字段太多,而且影响效率)
字段不全
欠取是指从不包含所需所有信息的 REST API 获取响应,迫使您向不同的端点发出多个请求,直到满足所有数据需求。例如,如果我们的应用有一个主页,我们希望在其中显示我们的每日新版本、用户个人资料图片和姓名以及他们最常播放的播放列表,我们仅使用 /browse/new-release 端点无法执行此操作。这并不包含我们需要的所有信息。因此,我们必须向 api.spotify.com/v1/browse/new-releases 提出请求,向 api.spotify.com/v1/me 发出请求,向 api.spotify.com/v1/me/playlists 提出请求。
所有这些请求都是相互独立的,有些请求可能需要比其他请求更长的时间才能完成,让您等待所有数据正确绘制屏幕。
欠取是有问题的,因为您不仅必须发出多个请求,这会消耗您的应用程序快速加载的能力,而且这些多个请求都需要不分青红皂白的时间才能完成。
解释:(需要请求多个接口在前端拼接数据,会影响页面加载速度)
GraphQL
这就是 GraphQL 的强大之处。GraphQL 是 API 的一种查询语言,它允许您以声明方式获取数据 - 也就是说,您可以准确地告诉它您想要什么数据,它会返回这些数据。不多也不少。无需使用为你预定义的刚性终结点,您可以编写自定义查询来接收所需的数据。相当于 GET 的 GraphQL 是一个查询,而突变相当于 POST、PUT、DELETE 或 PATCH。查询只是获取信息,突变更新数据,然后获取新更新的数据。您可以将 GraphQL 与任何编程语言一起使用,甚至可以从多个来源获取数据。
Schema
作为开发人员,您设计了定义 GraphQL 服务器功能的 GraphQL 模式 - 将其视为字典。在架构中,定义可以查询的所有可能数据。在架构中,定义对象类型,这些对象类型表示可以获取的一种对象以及可以请求的有关该对象的所有属性。我们项目的架构可能如下所示:
type Album {
artistName: String!
numberOfSongs: Int!
coverArtLink: String!
}
type Playlist {
name: String!
coverArtLink: String!
totalDuration: String!
}
type UserInfo {
name: String!
profilePicLink: String!
likedSongs: Int!
topGenre: String!
}
Queries
从理论上讲,我们会描述一堆其他对象类型来表示我们需要为应用程序查询的所有内容,然后我们可以编写自定义查询来获取这些对象!现在,我们可以编写一个查询来获取应用主页的新专辑、用户个人资料信息及其播放列表,而不是使用 REST API 发出多个请求:
{
query homepageView {
Album {
artistName
numberOfSongs
coverArtLink
}
UserInfo {
name
profilePicLink
}
Playlist {
name
coverArtLink
}
}
}
通过这一个Queries,我们能够请求我们的应用程序所需的所有信息,这些信息需要使用 REST API 进行 3 个单独的请求!我们也只获取我们需要的确切内容,而不是依赖于获取每个对象上存在的每个属性,并且我们不会被应用程序不需要的一堆额外数据所困扰。响应如下所示:
{
"homepageView" {
Album {
"Kendrick Lamar"
10
"assets/artists/kendrick_lamar"
}
UserInfo {
"Camila Ramos"
"assets/users/camila_ramos/photo"
}
Playlist {
"Today's Chill"
"assets/playlists/todays_chill"
}
}
}
您会注意到响应的形状与查询相同。因为我们要求提供非常具体的数据,所以我们总是可以预测我们的反应会是什么样子。
Resolvers
你告诉 GraphQL 如何提出你的查询的答案。在模式中,您已经定义了每个字段的响应类型,但解析器是您告诉 GraphQL 如何提出数据的地方。您定义的每种类型的每个字段都有一个由您(开发人员)编写的解析程序函数。查询字段时,将调用相应的解析程序以生成对查询的响应。下面是 GraphQL 文档中的一个示例,介绍了 JS 中的Resolvers如何查找名为“human”的类型。
Query: {
human(obj, args, context, info) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
结语
REST API 的五个主要问题是
定义端点
字段冗余
多个请求拼接数据
我们看到了 GraphQL 如何解决所有这些问题,并使开发人员的体验提高 100 倍。