无论是创建网站,还是移动应用程序,我们都需要通过 API 来传递数据,通过 API 我们可以获取到数据库中的数据,可以操作数据库,可以处理一些业务逻辑。现在最流行的 API 架构是 REST。但是,GraphQL 正在逐渐追赶着它。
GraphQL 是一种新型的 API 架构,它比 REST 更灵活、更高效,并且具有声明式数据获取等功能。虽然 GraphQL 正在变得非常流行,但它并没有取代 REST,因为一些用户发现它更难使用,并认为它是一个过度设计的解决方案,尤其是对于一些小型项目。
REST
现代应用程序开发中 API 的主要架构是 REST。大多数后端框架可以非常容易地实现 REST。REST API 通常通过 HTTP 方法被调用。通过访问一个 URL, 就实现了对接口的调用处理。
REST 案例
假设你正在创建一个博客站点, 在首页上,你会显示最新文章的摘要,包括标题、图片和简短描述。为了提供这些数据,你需要在后端服务器上查询数据库或者缓存来获取结果。然后一个 REST API 就完成了 GET/api/articles,它以 JSON 数组的形式返回所需的数据,如下例所示:
// GET /articles
[
{
"id": 1,
"title": "REST is Awesome",
"image": "https://restblog.com/img/dsh9a89.png",
"description": "The benefits of REST"
},
{
"id": 2,
"title": "How REST Works",
"image": "https://restblog.com/img/33szad2.png",
"description": "Learn about REST"
}
]
REST 的优点
方便实现
在 Web 服务器应用程序中设置 REST 很简单,尤其是当我使用一些框架的时候。比如laravel,express,django,springboot 等,它们都提供了非常方便的方法来实现 REST 接口。
例如,/api/articles 使用 MongoDB 在 Express 应用程序中设置 REST 接口非常简单:
app.get('/api/articles', async (req, res) => {
try {
const articles = await db.articles.find() res.json(articles)
} catch (err) {
res.status(500).send(err)
}
})
通俗易懂
REST 很好理解,基本上通过请求方法和请求参数还有接口名称,我们就知道这个接口的作用,并且无论是前端人员还是后台人员都可以非常容易地通过接口文档进行数据的交互。
REST 的缺点
冗余数据
回到博客的例子,假设我们在创建 PC 站点的同时,也创建了一个移动网站。和桌面版本一样,在移动端的首页我们也要显示文章摘要。由于手机屏幕尺寸较小,这里的摘要只需要标题和图片,可以省略描述。
但不幸的是,由于/api/articles 接口是固定的,所以移动端的 description 在调用 API 是否仍然会收到该字段。
这些冗余数据在频繁调用和发送大量数据的时候会造成服务器的资源浪费。
嵌套数据
有些时候我们通过一个接口要返回更多的数据的时候,我们就会使用嵌套数据。
例如,我们可能需要一个带有嵌套评论的文章。我们在获取到文章的时候,还需要再通过文章id获取评论信息。这就会导致请求时间的延长。
GraphQL
REST 数据冗余和低效率,促使 Facebook 工程师在 2015 创建了一种新的 API 设计模式,称为 GraphQL。与 REST 一样,GraphQL 不是特定的软件,而是 API 设计的规范。
GraphQL 的工作原理
为了了解 GraphQL 的优势,我们将快速概述它的工作原理。与 REST 不同,GraphQL 需要一个模式来告诉客户端和服务器通过 API 允许哪些数据和操作。这些是用 GraphQL 模式是语言定义的,它是一种与语言无关的具有强大的类型系统的格式。
GraphQL 例子
让我们回到获取文章和评论的例子中。在我们的 GraphQL 模式中,我们将定义Article类型,该类型具有必需的整数id字段和用于title、image和可选字符串字段description,如下所示:
type Article {
id: Integer!
title: String
image: String
description: String
}
除了基本的标量类型之外,模式对象还可以相互引用。我们可以在类型和类型之间创建一对多的关系Comment,如下所示:
type Article {
id: Integer!
title: String
image: String
description: String
comments: [Comment]
}
type Comment {
content: String
article: Article
author: Author
}
定义操作
GraphQL 模式的另一个重要用途是定义操作,包括读取数据的查询和写入数据。在这里,我们提供了一个查询Articles:
type Article {
id: Integer!
title: String
image: String
description: String
comments: [Comment]
}
type Comment {
content: String
article: Article
author: Author
}
type Query {
articles: [Article]
}
GraphQL 的优点
声明式数据获取
GraphQL 杀手级功能是声明式数据获取,客户端可以在其中准确指定它需要的数据。这可以包括特定字段,甚至在嵌套对象中。我们之前看到必须在模式上定义操作。但是,在这些操作中,我们可以指定我们希望查询返回到模式限制的哪些字段。
例如,我们可以创建一个查询,Articles只获取我们想要的字段,无论是否嵌套Comments。请参见下面的示例:
query {
articles {
id
title
image
description
comments {
content
}
}
}
这是将从该查询返回的数据结构。请注意,在 GraphQL 响应中接收到的数据将与请求它的查询具有相同的结构。
{
"data": {
"articles": [
{
"id": 1,
"title": "REST is Awesome",
"image": "https://restblog.com/img/dsh9a8.png",
"description": "An article about REST",
"comments": [
{
"content": "GraphQL is better!"
}]
}
}
通过这种方式,GraphQL 消除了冗余数据和嵌套数据问题。
健壮性
由于强类型和预定义查询的要求,GraphQL 可以提供开箱即用的验证和类型检查。反过来,这意味着 GraphQL 本质上是自记录的。一旦字段、类型或查询发生更改,基于架构的文档可以自动更新。
没有版本控制的 API
每次应用更改时,API 可能也需要更改。例如,假设我们决定将实体中的description字段重命名的时候.
REST 通过提供多个版本来处理这个问题,这对于 API 开发人员来说是很麻烦的。
使用 GraphQL,可以从模式中删除不推荐使用的字段,而不会影响现有查询。这为应用程序提供了对新功能的持续访问,并鼓励更清洁、更可维护的代码。
GraphQL 的缺点
矫枉过正
一些开发人员认为 GraphQL 解决的问题通常被夸大了。例如,对于大多数小型应用程序来说,因为几个字节的冗余数据而设计的更加复杂,这可能并不划算。
难于学习
GraphQL 比 REST 更难于实现,它为新用户提供了更难的学习曲线。
难以缓存
GraphQL 经常被批评为更难缓存。REST 客户端受益于 HTTP 缓存,因为所有端点都是 URL,而 GraphQL 客户端需要实现自己的自定义解决方案。
总结
虽然 REST 架构在过去十年中主导了 Web 开发,但它对接口调用的的使用使其在某些情况下有些不灵活且效率低下。GraphQL 通过提供严格类型化的模式语言来解决这些问题,接口调用者可以根据自己的需要进行查询。
如果未来能有更好的设计将两者的优点结合,我相信会是最佳的解决方案。