GraphQL vs REST – GraphQL与REST

最后修改: 2022年 2月 22日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When creating web services to support our applications, we may choose to use REST or GraphQL as the pattern for communication. While both are most likely to use JSON over HTTP, they have different advantages and disadvantages.

在创建Web服务以支持我们的应用程序时,我们可能会选择使用REST或GraphQL作为通信的模式。虽然两者都最有可能通过HTTP使用JSON,但它们有不同的优点和缺点。

In this tutorial, we’ll compare GraphQL with REST. We’ll create a product database example and compare how the two solutions vary when executing the same client operations.

在本教程中,我们将比较GraphQL与REST。我们将创建一个产品数据库的例子,并比较这两种解决方案在执行相同的客户端操作时有何不同。

2. Example Service

2.服务实例

Our example service will allow us to:

我们的实例服务将使我们能够。

  • Create a product in draft status
  • Update the product details
  • Get the list of products
  • Get details of a single product along with its orders

Let’s begin by creating the application using REST.

让我们开始使用REST创建应用程序。

3. REST

3.REST

REST (Representational State Transfer) is an architectural style for distributed hypermedia systems. The primary data element in REST is called Resource. In this example, the resource is “product”.

REST(Representational State Transfer)是一种分布式超媒体系统的架构风格。REST中的主要数据元素被称为资源。在这个例子中,该资源是“产品”

3.1. Create Product

3.1.创建产品

To create a product, we’ll use a POST method on our API:

为了创建一个产品,我们将在我们的API上使用一个POST方法。

curl --request POST 'http://localhost:8081/product' \
--header 'Content-Type: application/json' \
--data '{
  "name": "Watch",
  "description": "Special Swiss Watch",
  "status": "Draft",
  "currency": "USD",
  "price": null,
  "imageUrls": null,
  "videoUrls": null,
  "stock": null,
  "averageRating": null
}'

As a result, the system will create a new product.

因此,系统将创建一个新产品。

3.2. Update Product

3.2.更新产品

In REST, we conventionally update products using a PUT method:

在REST中,我们习惯于使用PUT方法来更新产品。

curl --request PUT 'http://localhost:8081/product/{product-id}' \
--header 'Content-Type: application/json' \
--data '{
    "name": "Watch",
    "description": "Special Swiss Watch",
    "status": "Draft",
    "currency": "USD",
    "price": 1200.0,
    "imageUrls": [
        "https://graphqlvsrest.com/imageurl/product-id"
    ],
    "videoUrls": [
        "https://graphqlvsrest.com/videourl/product-id"
    ],
    "stock": 10,
    "averageRating": 0.0
}'

As a result, there will be an update on the {product-id} object.

因此,{product-id}对象上会有一个更新。

3.3. Get Product List

3.3.获取产品列表

Listing products would normally be a GET operation, where we may use a query string to specify pagination:

列出产品通常是一个GET操作,我们可以使用一个查询字符串来指定分页。

curl --request GET 'http://localhost:8081/product?size=10&page=0'

The first response object is:

第一个响应对象是。

{
  "id": 1,
  "name": "T-Shirt",
  "description": "Special beach T-Shirt",
  "status": Published,
  "currency": "USD",
  "price": 30.0,
  "imageUrls": ["https://graphqlvsrest.com/imageurl/1"], 
  "videoUrls": ["https://graphqlvsrest.com/videourl/1"], 
  "stock": 10, 
  "averageRating": 3.5 
}

3.4. Get Single Product with Orders

3.4.用订单获取单一产品

To get a product and its order, we might normally expect to get a list of products with the previous API and then make calls to an order resource to find related orders:

为了获得一个产品和它的订单,我们通常会期望通过之前的API获得一个产品列表,然后调用一个order资源来寻找相关的订单。

curl --request GET 'localhost:8081/order?product-id=1'

The first response object is:

第一个响应对象是。

{
  "id": 1,
  "productId": 1,
  "customerId": "de68a771-2fcc-4e6b-a05d-e30a8dd0d756",
  "status": "Delivered",
  "address": "43-F 12th Street",
  "creationDate": "Mon Jan 17 01:00:18 GST 2022"
}

As we’re querying for orders by product-id, it makes sense to supply the id as a query parameter to the GET operation. We should note, however, that we would need to execute this operation once for each product we were interested in, on top of the original operation to fetch all the products. This is related to the N + 1 Select Problem.

由于我们是通过product-id来查询订单的,提供id作为GET操作的查询参数是有意义的。然而,我们应该注意到,我们将需要为我们感兴趣的每个产品执行一次这个操作,在原始操作的基础上获取所有产品。这与N + 1选择问题有关。

4. GraphQL

4.GraphQL

GraphQL is a query language for APIs that comes with a framework for fulfilling those queries using existing data services.

GraphQL是一种用于API的查询语言,它带有一个框架,可以使用现有的数据服务完成这些查询。

The building blocks of GraphQL are queries and mutations. A query is responsible for fetching data, while mutations are used for creation and update.

GraphQL的构建模块是查询和变异。查询负责获取数据,而突变则用于创建和更新。

Both query and mutation define a schema. The schema defines the possible client requests and responses.

查询和突变都定义了一个schema。该模式定义了可能的客户端请求和响应。

Let’s re-implement our example using GraphQL Server.

让我们使用GraphQL服务器重新实现我们的例子。

4.1. Create Product

4.1.创建产品

Let’s use a mutation named saveProduct:

让我们使用一个名为saveProduct的突变。

curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
  "query": "mutation {saveProduct (
    product: {
      name: \"Bed-Side Lamp\",
      price: 24.0,
      status: \"Draft\",
      currency: \"USD\"
    }){ id name currency price status}
  }"
}'

In this saveProduct function, everything inside the round brackets is the input type schema. The curly brackets after this describe the fields to be returned by the server.

在这个saveProduct函数中,圆括号内的所有内容都是输入类型架构。后面的大括号描述了将由服务器返回的字段

When we run the mutation, we should expect a response with the chosen fields:

当我们运行突变时,我们应该期待一个带有所选字段的响应。

{
  "data": {
    "saveProduct": {
      "id": "12",
      "name": "Bed-Side Lamp",
      "currency": "USD",
      "price": 24.0,
      "status": "Draft"
    }
  }
}

This request is very similar to the POST request we made with the REST version, though we can now customize the response a little.

这个请求与我们在REST版本中的POST请求非常相似,尽管我们现在可以对响应进行一些定制。

4.2. Update Product

4.2.更新产品

Similarly, we can use another mutation named updateProduct to modify a product:

同样地,我们可以使用另一个名为updateProduct的突变来修改一个产品。

curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{"query": "mutation {updateProduct(
    id: 11
    product: {
      price: 14.0,
      status: \"Publish\"
    }){ id name currency price status }  
  }","variables":{}}'

We receive the chosen fields in the response:

我们在回复中收到所选择的字段。

{
  "data": {
    "updateProduct": {
      "id": "12",
      "name": "Bed-Side Lamp",
      "currency": "USD",
      "price": 14.0,
      "status": "Published"
    }
  }
}

As we can see, GraphQL provides flexibility over the format of responses.

正如我们所看到的,GraphQL对响应的格式提供了灵活性

4.3. Get Product List

4.3.获取产品列表

To fetch the data from the server, we’ll make use of a query:

为了从服务器上获取数据,我们将使用一个查询。

curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
    "query": "query {products(size:10,page:0){id name status}}"
}'

Here, we’ve also described the page of results we want to see:

在这里,我们也描述了我们想要看到的结果页面。

{
  "data": {
    "products": [
      {
        "id": "1",
        "name": "T-Shirt",
        "status": "Published"
      },
      ...
    ]
  }
}

4.4. Get Single Product with Orders

4.4.用订单获取单一产品

With GraphQL, we can ask the GraphQL server to join together the products and the orders:

通过GraphQL,我们可以要求GraphQL服务器将产品和订单连接起来。

curl --request POST 'http://localhost:8081/graphql' \
--header 'Content-Type: application/json' \
--data \
'{
    "query": "query {product(id:1){ id name orders{customerId address status creationDate}}}"
}'

In this query, we fetch the product with an id equal to 1 along with its orders. This enables us to make the request in a single operation, Let’s check the response:

在这个查询中,我们获取了id等于1的产品和它的订单。这使我们能够在一个单一的操作中进行请求,让我们检查一下响应。

{
  "data": {
    "product": {
      "id": "1",
      "name": "T-Shirt",
      "orders": [
        {
          "customerId": "de68a771-2fcc-4e6b-a05d-e30a8dd0d756",
          "status": "Delivered",
          "address": "43-F 12th Street",
          "creationDate": "Mon Jan 17 01:00:18 GST 2022"
        }, 
        ...
      ]
    }
  }
}

As we can see here, the response has the product’s details and its respective orders.

正如我们在这里看到的,响应有产品的细节和它各自的订单。

To achieve this with REST, we were required to send a couple of requests – the first to get the product and the second to fetch the respective orders.

为了用REST实现这一点,我们需要发送几个请求–第一个请求是获取产品,第二个请求是获取相应的订单。

5. Comparison

5.比较

These examples show how the use of GraphQL reduces the amount of traffic between the client and server and allows the client to provide some formatting rules for the responses.

这些例子显示了GraphQL的使用如何减少客户端和服务器之间的流量,并允许客户端为响应提供一些格式化规则。

It’s worth noting that the data source behind these APIs may still have to execute the same operations to modify or fetch data, but the richness of the interface between client and server allows the client to do less work with GraphQL.

值得注意的是,这些API背后的数据源可能仍然需要执行相同的操作来修改或获取数据,但客户端和服务器之间丰富的接口使客户端可以用GraphQL做更少的工作。

Let’s compare the two approaches further.

让我们进一步比较这两种方法。

5.1. Flexible and Dynamic

5.1.灵活和动态

GraphQL allows flexible and dynamic queries:

GraphQL允许灵活和动态的查询。

  • Client-side applications can request only the required fields
  • Aliases can be used to request fields with custom keys
  • The client can use the query to manage results order
  • The client can be better decoupled from any changes in the API, as there’s no single version of the response object’s structure to follow

5.2. Less Expensive Operations

5.2.费用较低的业务

Every server request has a price of round-trip time and payload size.

每个服务器请求都有一个往返时间和有效载荷大小的价格。

In REST, we may end up sending multiple requests to achieve the required functionality. These multiple requests will be an expensive operation. Also, the response payload may have unnecessary data that may not be required by the client-side application.

在REST中,我们最终可能会发送多个请求来实现所需的功能。这些多次请求将是一个昂贵的操作。另外,响应的有效载荷可能有不必要的数据,而这些数据可能不是客户端应用程序所需要的。

GraphQL tends to avoid expensive operations. We can often fetch all the data we need in a single request using GraphQL.

GraphQL倾向于避免昂贵的操作。我们通常可以使用GraphQL在一个请求中获取我们需要的所有数据

5.3. When to Use REST?

5.3.什么时候使用REST?

GraphQL is not a replacement for REST. Both can even co-exist in the same application. The increased complexity of hosting GraphQL endpoints may be worth it, depending on the use case.

GraphQL 并非 REST 的替代品。两者甚至可以在同一应用中共存。根据使用情况,托管GraphQL端点所增加的复杂性可能是值得的。

We may prefer REST when:

在以下情况下,我们可能更喜欢REST。

  • Our applications are naturally resource-driven, where the operations are very directly and wholly linked with the individual resource entities
  • There’s a need for web caching, as GraphQL does not natively support it
  • We require file uploads, as GraphQL doesn’t natively support it

6. Conclusion

6.结语

In this article, we compared REST and GraphQL using a practical example.

在这篇文章中,我们用一个实际的例子比较了REST和GraphQL。

We saw how we might conventionally use each approach learned.

我们看到了我们如何按惯例使用所学的每一种方法。

Then, we discussed how neither approach has a clear advantage over the other. Our requirements will be the driving force in making a choice between them. Occasionally, both can co-exist as well.

然后,我们讨论了两种方法都没有明显的优势。我们的要求将是在它们之间做出选择的驱动力。偶尔,两者也可以共存。

As always, the example code for this article is available over on GitHub.

像往常一样,本文的示例代码可在GitHub上获得